From 1e9a8aae48ffbf08faea4641834fb4e88a9670c6 Mon Sep 17 00:00:00 2001 From: "kaf24@scramble.cl.cam.ac.uk" Date: Wed, 3 Dec 2003 10:09:26 +0000 Subject: [PATCH] bitkeeper revision 1.641 (3fcdb656yM6DlMdwk-janNkBhdceiQ) Many files: new file megaraid.c, Makefile, Rules.mk: New Fusion driver. --- .rootkeys | 20 + xen/Rules.mk | 1 + xen/drivers/Makefile | 2 + xen/drivers/message/fusion/Config.in | 38 + xen/drivers/message/fusion/Makefile | 11 + xen/drivers/message/fusion/isense.h | 95 + xen/drivers/message/fusion/linux_compat.h | 295 + xen/drivers/message/fusion/lsi/fc_log.h | 89 + xen/drivers/message/fusion/lsi/mpi.h | 678 ++ xen/drivers/message/fusion/lsi/mpi_cnfg.h | 1569 ++++ xen/drivers/message/fusion/lsi/mpi_fc.h | 362 + .../message/fusion/lsi/mpi_history.txt | 276 + xen/drivers/message/fusion/lsi/mpi_init.h | 318 + xen/drivers/message/fusion/lsi/mpi_ioc.h | 698 ++ xen/drivers/message/fusion/lsi/mpi_lan.h | 212 + xen/drivers/message/fusion/lsi/mpi_raid.h | 225 + xen/drivers/message/fusion/lsi/mpi_targ.h | 429 + xen/drivers/message/fusion/lsi/mpi_type.h | 91 + xen/drivers/message/fusion/mptbase.c | 6130 +++++++++++++ xen/drivers/message/fusion/mptbase.h | 1123 +++ xen/drivers/message/fusion/mptscsih.c | 7948 +++++++++++++++++ xen/drivers/message/fusion/mptscsih.h | 343 + xen/drivers/message/fusion/scsi3.h | 707 ++ xen/drivers/scsi/megaraid.c | 8 +- 24 files changed, 21665 insertions(+), 3 deletions(-) create mode 100644 xen/drivers/message/fusion/Config.in create mode 100644 xen/drivers/message/fusion/Makefile create mode 100644 xen/drivers/message/fusion/isense.h create mode 100644 xen/drivers/message/fusion/linux_compat.h create mode 100644 xen/drivers/message/fusion/lsi/fc_log.h create mode 100644 xen/drivers/message/fusion/lsi/mpi.h create mode 100644 xen/drivers/message/fusion/lsi/mpi_cnfg.h create mode 100644 xen/drivers/message/fusion/lsi/mpi_fc.h create mode 100644 xen/drivers/message/fusion/lsi/mpi_history.txt create mode 100644 xen/drivers/message/fusion/lsi/mpi_init.h create mode 100644 xen/drivers/message/fusion/lsi/mpi_ioc.h create mode 100644 xen/drivers/message/fusion/lsi/mpi_lan.h create mode 100644 xen/drivers/message/fusion/lsi/mpi_raid.h create mode 100644 xen/drivers/message/fusion/lsi/mpi_targ.h create mode 100644 xen/drivers/message/fusion/lsi/mpi_type.h create mode 100644 xen/drivers/message/fusion/mptbase.c create mode 100644 xen/drivers/message/fusion/mptbase.h create mode 100644 xen/drivers/message/fusion/mptscsih.c create mode 100644 xen/drivers/message/fusion/mptscsih.h create mode 100644 xen/drivers/message/fusion/scsi3.h diff --git a/.rootkeys b/.rootkeys index b3264c9811..23ce80d113 100644 --- a/.rootkeys +++ b/.rootkeys @@ -169,6 +169,26 @@ 3ddb79bdkDY1bSOYkToP1Cc49VdBxg xen/drivers/ide/ide.c 3ddb79bdPyAvT_WZTAFhaX0jp-yXSw xen/drivers/ide/ide_modes.h 3e4a8d401aSwOzCScQXR3lsmNlAwUQ xen/drivers/ide/piix.c +3fcdb64eEYjJwVTDdATmditDL6GreA xen/drivers/message/fusion/Config.in +3fcdb64ee9dgWh8x0sR-le703mbyaQ xen/drivers/message/fusion/Makefile +3fcdb64e_e-c5pG7ev4Ebpc1TSyqBA xen/drivers/message/fusion/isense.h +3fcdb64ekZ3CRQVYOvtBY-jcdWLxFg xen/drivers/message/fusion/linux_compat.h +3fcdb64e_ar-OB7FSvk8aIOQeETx-Q xen/drivers/message/fusion/lsi/fc_log.h +3fcdb64eSDjgD3W3MypMOae50Fe4BA xen/drivers/message/fusion/lsi/mpi.h +3fcdb64e9iLTo9Etbdxrr_dC-1bb5g xen/drivers/message/fusion/lsi/mpi_cnfg.h +3fcdb64egl5jNkfKyC7Co6NNNsfY4w xen/drivers/message/fusion/lsi/mpi_fc.h +3fcdb64edETmgJSw8KhkrQgyY67BTA xen/drivers/message/fusion/lsi/mpi_history.txt +3fcdb64eHxLXhIqEfYi8_UPf4j7Kng xen/drivers/message/fusion/lsi/mpi_init.h +3fcdb64fG8Mm8Jt4Il-jsVcKTfRTpg xen/drivers/message/fusion/lsi/mpi_ioc.h +3fcdb64fjt9nuJga7t5dRK1-FCrURQ xen/drivers/message/fusion/lsi/mpi_lan.h +3fcdb64fnnYbvcLibeLTNR81zMi8Lw xen/drivers/message/fusion/lsi/mpi_raid.h +3fcdb64fpRM8NpPlaagM3dQ1jW3YNQ xen/drivers/message/fusion/lsi/mpi_targ.h +3fcdb64fXgeuSY_4Uy_OWSyjWlwnaQ xen/drivers/message/fusion/lsi/mpi_type.h +3fcdb64fQjcOsOCcCMQSBgA0kuttPA xen/drivers/message/fusion/mptbase.c +3fcdb64fz22QsBDCmRylGcrYz2X2YQ xen/drivers/message/fusion/mptbase.h +3fcdb64fqClM55cDs-jlCSJ7fhqmqg xen/drivers/message/fusion/mptscsih.c +3fcdb64fTVgpQj9UQw5aHN_eUHlzGQ xen/drivers/message/fusion/mptscsih.h +3fcdb64fl9djvIFAYQozgMVDUNPIkQ xen/drivers/message/fusion/scsi3.h 3ddb79bfMlOcWUwjtg6oMYhGySHDDw xen/drivers/net/3c59x.c 3ddb79c0tWiE8xIFHszxipeVCGKTSA xen/drivers/net/Makefile 3f0c4247730LYUgz3p5ziYqy-s_glw xen/drivers/net/SUPPORTED_CARDS diff --git a/xen/Rules.mk b/xen/Rules.mk index a34a740f76..82d459f933 100644 --- a/xen/Rules.mk +++ b/xen/Rules.mk @@ -23,6 +23,7 @@ ALL_OBJS += $(BASEDIR)/drivers/block/driver.o ALL_OBJS += $(BASEDIR)/drivers/cdrom/driver.o ALL_OBJS += $(BASEDIR)/drivers/ide/driver.o ALL_OBJS += $(BASEDIR)/drivers/scsi/driver.o +ALL_OBJS += $(BASEDIR)/drivers/message/fusion/driver.o ALL_OBJS += $(BASEDIR)/arch/$(ARCH)/arch.o HOSTCC = gcc diff --git a/xen/drivers/Makefile b/xen/drivers/Makefile index 814ed909d8..2d969e41be 100644 --- a/xen/drivers/Makefile +++ b/xen/drivers/Makefile @@ -7,6 +7,7 @@ default: $(MAKE) -C cdrom $(MAKE) -C ide $(MAKE) -C scsi + $(MAKE) -C message/fusion clean: $(MAKE) -C char clean @@ -16,3 +17,4 @@ clean: $(MAKE) -C cdrom clean $(MAKE) -C ide clean $(MAKE) -C scsi clean + $(MAKE) -C message/fusion clean diff --git a/xen/drivers/message/fusion/Config.in b/xen/drivers/message/fusion/Config.in new file mode 100644 index 0000000000..39274207f6 --- /dev/null +++ b/xen/drivers/message/fusion/Config.in @@ -0,0 +1,38 @@ +mainmenu_option next_comment +comment 'Fusion MPT device support' + +dep_tristate "Fusion MPT (base + ScsiHost) drivers" CONFIG_FUSION $CONFIG_SCSI $CONFIG_BLK_DEV_SD + +if [ "$CONFIG_FUSION" = "y" -o "$CONFIG_FUSION" = "m" ]; then + + if [ "$CONFIG_BLK_DEV_SD" = "y" -a "$CONFIG_FUSION" = "y" ]; then + define_bool CONFIG_FUSION_BOOT y + else + define_bool CONFIG_FUSION_BOOT n + fi + int " Maximum number of scatter gather entries" CONFIG_FUSION_MAX_SGE 40 + + if [ "$CONFIG_MODULES" = "y" ]; then + # How can we force these options to module or nothing? + dep_tristate " Enhanced SCSI error reporting" CONFIG_FUSION_ISENSE $CONFIG_FUSION m + dep_tristate " Fusion MPT misc device (ioctl) driver" CONFIG_FUSION_CTL $CONFIG_FUSION m + fi + + dep_tristate " Fusion MPT LAN driver" CONFIG_FUSION_LAN $CONFIG_FUSION $CONFIG_NET + if [ "$CONFIG_FUSION_LAN" != "n" ]; then + define_bool CONFIG_NET_FC y + fi + +else + + define_bool CONFIG_FUSION_BOOT n + # These be define_tristate, but we leave them define_bool + # for backward compatibility with pre-linux-2.2.15 kernels. + # (Bugzilla:fibrebugs, #384) + define_bool CONFIG_FUSION_ISENSE n + define_bool CONFIG_FUSION_CTL n + define_bool CONFIG_FUSION_LAN n + +fi + +endmenu diff --git a/xen/drivers/message/fusion/Makefile b/xen/drivers/message/fusion/Makefile new file mode 100644 index 0000000000..3e30f33875 --- /dev/null +++ b/xen/drivers/message/fusion/Makefile @@ -0,0 +1,11 @@ + +include $(BASEDIR)/Rules.mk + +# naive OBJS rule gets link order wrong +FUSIONOBJS := mptbase.o mptscsih.o + +default: $(OBJS) + $(LD) -r -o driver.o $(FUSIONOBJS) + +clean: + rm -f *.o *~ core diff --git a/xen/drivers/message/fusion/isense.h b/xen/drivers/message/fusion/isense.h new file mode 100644 index 0000000000..e1ce503fef --- /dev/null +++ b/xen/drivers/message/fusion/isense.h @@ -0,0 +1,95 @@ +#ifndef ISENSE_H_INCLUDED +#define ISENSE_H_INCLUDED +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#ifdef __KERNEL__ +#include /* needed for u8, etc. */ +#include /* needed for strcat */ +#include /* needed for sprintf */ +#else + #ifndef U_STUFF_DEFINED + #define U_STUFF_DEFINED + typedef unsigned char u8; + typedef unsigned short u16; + typedef unsigned int u32; + #endif +#endif + +#include "scsi3.h" /* needed for all things SCSI */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Defines and typedefs... + */ + +#ifdef __KERNEL__ +#define PrintF(x) printk x +#else +#define PrintF(x) printf x +#endif + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#define RETRY_STATUS ((int) 1) +#define PUT_STATUS ((int) 0) + +/* + * A generic structure to hold info about IO request that caused + * a Request Sense to be performed, and the resulting Sense Data. + */ +typedef struct IO_Info +{ + char *DevIDStr; /* String of chars which identifies the device. */ + u8 *cdbPtr; /* Pointer (Virtual/Logical addr) to CDB bytes of + IO request that caused ContAllegianceCond. */ + u8 *sensePtr; /* Pointer (Virtual/Logical addr) to Sense Data + returned by Request Sense operation. */ + u8 *dataPtr; /* Pointer (Virtual/Logical addr) to Data buffer + of IO request caused ContAllegianceCondition. */ + u8 *inqPtr; /* Pointer (Virtual/Logical addr) to Inquiry Data for + IO *Device* that caused ContAllegianceCondition. */ + u8 SCSIStatus; /* SCSI status byte of IO request that caused + Contingent Allegiance Condition. */ + u8 DoDisplay; /* Shall we display any messages? */ + u16 rsvd_align1; + u32 ComplCode; /* Four-byte OS-dependent completion code. */ + u32 NotifyL; /* Four-byte OS-dependent notification field. */ +} IO_Info_t; + +/* + * SCSI Additional Sense Code and Additional Sense Code Qualifier table. + */ +typedef struct ASCQ_Table +{ + u8 ASC; + u8 ASCQ; + char *DevTypes; + char *Description; +} ASCQ_Table_t; + +#if 0 +/* + * SCSI Opcodes table. + */ +typedef struct SCSI_OPS_Table +{ + u8 OpCode; + char *DevTypes; + char *ScsiCmndStr; +} SCSI_OPS_Table_t; +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Public entry point prototypes + */ + +/* in scsiherr.c, needed by mptscsih.c */ +extern int mpt_ScsiHost_ErrorReport(IO_Info_t *ioop); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#endif + diff --git a/xen/drivers/message/fusion/linux_compat.h b/xen/drivers/message/fusion/linux_compat.h new file mode 100644 index 0000000000..1a798550b4 --- /dev/null +++ b/xen/drivers/message/fusion/linux_compat.h @@ -0,0 +1,295 @@ +/* drivers/message/fusion/linux_compat.h */ + +#ifndef FUSION_LINUX_COMPAT_H +#define FUSION_LINUX_COMPAT_H +/*{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include +#include +#include +#include + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#ifndef rwlock_init +#define rwlock_init(x) do { *(x) = RW_LOCK_UNLOCKED; } while(0) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +#define SET_NICE(current,x) do {(current)->nice = (x);} while (0) +#else +#define SET_NICE(current,x) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) +#define pci_enable_device(pdev) (0) +#define SCSI_DATA_UNKNOWN 0 +#define SCSI_DATA_WRITE 1 +#define SCSI_DATA_READ 2 +#define SCSI_DATA_NONE 3 +#endif + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) +#define pci_set_dma_mask(pdev, mask) (0) +#define scsi_set_pci_device(sh, pdev) (0) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) + typedef unsigned int dma_addr_t; +# endif +#else +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,42) + typedef unsigned int dma_addr_t; +# endif +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) +/*{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +/* This block snipped from lk-2.2.18/include/linux/init.h { */ +/* + * Used for initialization calls.. + */ +typedef int (*initcall_t)(void); +typedef void (*exitcall_t)(void); + +#define __init_call __attribute__ ((unused,__section__ (".initcall.init"))) +#define __exit_call __attribute__ ((unused,__section__ (".exitcall.exit"))) + +extern initcall_t __initcall_start, __initcall_end; + +#define __initcall(fn) \ + static initcall_t __initcall_##fn __init_call = fn +#define __exitcall(fn) \ + static exitcall_t __exitcall_##fn __exit_call = fn + +#ifdef MODULE +/* These macros create a dummy inline: gcc 2.9x does not count alias + as usage, hence the `unused function' warning when __init functions + are declared static. We use the dummy __*_module_inline functions + both to kill the warning and check the type of the init/cleanup + function. */ +typedef int (*__init_module_func_t)(void); +typedef void (*__cleanup_module_func_t)(void); +#define module_init(x) \ + int init_module(void) __attribute__((alias(#x))); \ + static inline __init_module_func_t __init_module_inline(void) \ + { return x; } +#define module_exit(x) \ + void cleanup_module(void) __attribute__((alias(#x))); \ + static inline __cleanup_module_func_t __cleanup_module_inline(void) \ + { return x; } + +#else +#define module_init(x) __initcall(x); +#define module_exit(x) __exitcall(x); +#endif +/* } block snipped from lk-2.2.18/include/linux/init.h */ + +/* This block snipped from lk-2.2.18/include/linux/sched.h { */ +/* + * Used prior to schedule_timeout calls.. + */ +#define __set_current_state(state_value) do { current->state = state_value; } while (0) +#ifdef __SMP__ +#define set_current_state(state_value) do { __set_current_state(state_value); mb(); } while (0) +#else +#define set_current_state(state_value) __set_current_state(state_value) +#endif +/* } block snipped from lk-2.2.18/include/linux/sched.h */ + +/* procfs compat stuff... */ +#define proc_mkdir(x,y) create_proc_entry(x, S_IFDIR, y) + +/* MUTEX compat stuff... */ +#define DECLARE_MUTEX(name) struct semaphore name=MUTEX +#define DECLARE_MUTEX_LOCKED(name) struct semaphore name=MUTEX_LOCKED +#define init_MUTEX(x) *(x)=MUTEX +#define init_MUTEX_LOCKED(x) *(x)=MUTEX_LOCKED + +/* Wait queues. */ +#define DECLARE_WAIT_QUEUE_HEAD(name) \ + struct wait_queue * (name) = NULL +#define DECLARE_WAITQUEUE(name, task) \ + struct wait_queue (name) = { (task), NULL } + +#if defined(__sparc__) && defined(__sparc_v9__) +/* The sparc64 ioremap implementation is wrong in 2.2.x, + * but fixing it would break all of the drivers which + * workaround it. Fixed in 2.3.x onward. -DaveM + */ +#define ARCH_IOREMAP(base) ((unsigned long) (base)) +#else +#define ARCH_IOREMAP(base) ioremap(base) +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#else /* LINUX_VERSION_CODE must be >= KERNEL_VERSION(2,2,18) */ + +/* No ioremap bugs in >2.3.x kernels. */ +#define ARCH_IOREMAP(base) ioremap(base) + +/*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) */ + + +/* + * Inclined to use: + * #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) + * here, but MODULE_LICENSE defined in 2.4.9-6 and 2.4.9-13 + * breaks the rule:-( + */ +#ifndef MODULE_LICENSE +#define MODULE_LICENSE(license) +#endif + + +/* PCI/driver subsystem { */ +/* SAE: Why not just use Linux's PCI stuff */ +#if XENO_KILLED +/*#ifndef pci_for_each_dev*/ +#define pci_for_each_dev(dev) for((dev)=pci_devices; (dev)!=NULL; (dev)=(dev)->next) +#define pci_peek_next_dev(dev) ((dev)->next ? (dev)->next : NULL) +#define DEVICE_COUNT_RESOURCE 6 +#define PCI_BASEADDR_FLAGS(idx) base_address[idx] +#define PCI_BASEADDR_START(idx) base_address[idx] & ~0xFUL +/* + * We have to keep track of the original value using + * a temporary, and not by just sticking pdev->base_address[x] + * back. pdev->base_address[x] is an opaque cookie that can + * be used by the PCI implementation on a given Linux port + * for any purpose. -DaveM + */ +#define PCI_BASEADDR_SIZE(__pdev, __idx) \ +({ unsigned int size, tmp; \ + pci_read_config_dword(__pdev, PCI_BASE_ADDRESS_0 + (4*(__idx)), &tmp); \ + pci_write_config_dword(__pdev, PCI_BASE_ADDRESS_0 + (4*(__idx)), 0xffffffff); \ + pci_read_config_dword(__pdev, PCI_BASE_ADDRESS_0 + (4*(__idx)), &size); \ + pci_write_config_dword(__pdev, PCI_BASE_ADDRESS_0 + (4*(__idx)), tmp); \ + (4 - size); \ +}) +#else +/* SAE: Is Linux's PCI stuff not good enough? */ +#if XENO_KILLED + +#define pci_peek_next_dev(dev) ((dev) != pci_dev_g(&pci_devices) ? pci_dev_g((dev)->global_list.next) : NULL) +#define PCI_BASEADDR_FLAGS(idx) resource[idx].flags +#define PCI_BASEADDR_START(idx) resource[idx].start +#define PCI_BASEADDR_SIZE(dev,idx) (dev)->resource[idx].end - (dev)->resource[idx].start + 1 + +#endif +#endif /* } ifndef pci_for_each_dev */ + + +/* Compatability for the 2.3.x PCI DMA API. */ +/* SAE: No need for this */ +#if XENO_KILLED +/*#ifndef PCI_DMA_BIDIRECTIONAL*/ +/*{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#define PCI_DMA_BIDIRECTIONAL 0 +#define PCI_DMA_TODEVICE 1 +#define PCI_DMA_FROMDEVICE 2 +#define PCI_DMA_NONE 3 + +#ifdef __KERNEL__ +#include +/* Pure 2^n version of get_order */ +static __inline__ int __get_order(unsigned long size) +{ + int order; + + size = (size-1) >> (PAGE_SHIFT-1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} +#endif + +#define pci_alloc_consistent(hwdev, size, dma_handle) \ +({ void *__ret = (void *)__get_free_pages(GFP_ATOMIC, __get_order(size)); \ + if (__ret != NULL) { \ + memset(__ret, 0, size); \ + *(dma_handle) = virt_to_bus(__ret); \ + } \ + __ret; \ +}) + +#define pci_free_consistent(hwdev, size, vaddr, dma_handle) \ + free_pages((unsigned long)vaddr, __get_order(size)) + +#define pci_map_single(hwdev, ptr, size, direction) \ + virt_to_bus(ptr); + +#define pci_unmap_single(hwdev, dma_addr, size, direction) \ + do { /* Nothing to do */ } while (0) + +#define pci_map_sg(hwdev, sg, nents, direction) (nents) +#define pci_unmap_sg(hwdev, sg, nents, direction) \ + do { /* Nothing to do */ } while(0) + +#define sg_dma_address(sg) (virt_to_bus((sg)->address)) +#define sg_dma_len(sg) ((sg)->length) + +/*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#endif /* PCI_DMA_BIDIRECTIONAL */ + +/* + * With the new command queuing code in the SCSI mid-layer we no longer have + * to hold the io_request_lock spin lock when calling the scsi_done routine. + * For now we only do this with the 2.5.1 kernel or newer. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) + #define MPT_HOST_LOCK(flags) + #define MPT_HOST_UNLOCK(flags) +#else + #define MPT_HOST_LOCK(flags) \ + spin_lock_irqsave(&io_request_lock, flags) + #define MPT_HOST_UNLOCK(flags) \ + spin_unlock_irqrestore(&io_request_lock, flags) +#endif + +/* + * We use our new error handling code if the kernel version is 2.4.18 or newer. + * Remark: 5/5/03 use old EH code with 2.4 kernels as it runs in a background thread + * 2.4 kernels choke on a call to schedule via eh thread. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1) + #define MPT_SCSI_USE_NEW_EH +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,41) +#define mpt_work_struct work_struct +#define MPT_INIT_WORK(_task, _func, _data) INIT_WORK(_task, _func, _data) +#else +#define mpt_work_struct tq_struct +#define MPT_INIT_WORK(_task, _func, _data) \ +({ (_task)->sync = 0; \ + (_task)->routine = (_func); \ + (_task)->data = (void *) (_data); \ +}) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,28) +#define mptscsih_sync_irq(_irq) synchronize_irq(_irq) +#else +#define mptscsih_sync_irq(_irq) synchronize_irq() +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,58) +#define mpt_inc_use_count() +#define mpt_dec_use_count() +#else +#define mpt_inc_use_count() MOD_INC_USE_COUNT +#define mpt_dec_use_count() MOD_DEC_USE_COUNT +#endif + + +/*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#endif /* _LINUX_COMPAT_H */ + diff --git a/xen/drivers/message/fusion/lsi/fc_log.h b/xen/drivers/message/fusion/lsi/fc_log.h new file mode 100644 index 0000000000..dc98d46f90 --- /dev/null +++ b/xen/drivers/message/fusion/lsi/fc_log.h @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2000-2001 LSI Logic Corporation. All rights reserved. + * + * NAME: fc_log.h + * SUMMARY: MPI IocLogInfo definitions for the SYMFC9xx chips + * DESCRIPTION: Contains the enumerated list of values that may be returned + * in the IOCLogInfo field of a MPI Default Reply Message. + * + * CREATION DATE: 6/02/2000 + * ID: $Id: fc_log.h,v 4.6 2001/07/26 14:41:33 sschremm Exp $ + */ + + +/* + * MpiIocLogInfo_t enum + * + * These 32 bit values are used in the IOCLogInfo field of the MPI reply + * messages. + * The value is 0xabcccccc where + * a = The type of log info as per the MPI spec. Since these codes are + * all for Fibre Channel this value will always be 2. + * b = Specifies a subclass of the firmware where + * 0 = FCP Initiator + * 1 = FCP Target + * 2 = LAN + * 3 = MPI Message Layer + * 4 = FC Link + * 5 = Context Manager + * 6 = Invalid Field Offset + * 7 = State Change Info + * all others are reserved for future use + * c = A specific value within the subclass. + * + * NOTE: Any new values should be added to the end of each subclass so that the + * codes remain consistent across firmware releases. + */ +typedef enum _MpiIocLogInfoFc +{ + MPI_IOCLOGINFO_FC_INIT_BASE = 0x20000000, + MPI_IOCLOGINFO_FC_INIT_ERROR_OUT_OF_ORDER_FRAME = 0x20000001, /* received an out of order frame - unsupported */ + MPI_IOCLOGINFO_FC_INIT_ERROR_BAD_START_OF_FRAME = 0x20000002, /* Bad Rx Frame, bad start of frame primative */ + MPI_IOCLOGINFO_FC_INIT_ERROR_BAD_END_OF_FRAME = 0x20000003, /* Bad Rx Frame, bad end of frame primative */ + MPI_IOCLOGINFO_FC_INIT_ERROR_OVER_RUN = 0x20000004, /* Bad Rx Frame, overrun */ + MPI_IOCLOGINFO_FC_INIT_ERROR_RX_OTHER = 0x20000005, /* Other errors caught by IOC which require retries */ + MPI_IOCLOGINFO_FC_INIT_ERROR_SUBPROC_DEAD = 0x20000006, /* Main processor could not initialize sub-processor */ + MPI_IOCLOGINFO_FC_INIT_ERROR_RX_OVERRUN = 0x20000007, /* Scatter Gather overrun */ + MPI_IOCLOGINFO_FC_INIT_ERROR_RX_BAD_STATUS = 0x20000008, /* Receiver detected context mismatch via invalid header */ + MPI_IOCLOGINFO_FC_INIT_ERROR_RX_UNEXPECTED_FRAME= 0x20000009, /* CtxMgr detected unsupported frame type */ + MPI_IOCLOGINFO_FC_INIT_ERROR_LINK_FAILURE = 0x2000000A, /* Link failure occurred */ + MPI_IOCLOGINFO_FC_INIT_ERROR_TX_TIMEOUT = 0x2000000B, /* Transmitter timeout error */ + + MPI_IOCLOGINFO_FC_TARGET_BASE = 0x21000000, + MPI_IOCLOGINFO_FC_TARGET_NO_PDISC = 0x21000001, /* not sent because we are waiting for a PDISC from the initiator */ + MPI_IOCLOGINFO_FC_TARGET_NO_LOGIN = 0x21000002, /* not sent because we are not logged in to the remote node */ + MPI_IOCLOGINFO_FC_TARGET_DOAR_KILLED_BY_LIP = 0x21000003, /* Data Out, Auto Response, not sent due to a LIP */ + MPI_IOCLOGINFO_FC_TARGET_DIAR_KILLED_BY_LIP = 0x21000004, /* Data In, Auto Response, not sent due to a LIP */ + MPI_IOCLOGINFO_FC_TARGET_DIAR_MISSING_DATA = 0x21000005, /* Data In, Auto Response, missing data frames */ + MPI_IOCLOGINFO_FC_TARGET_DONR_KILLED_BY_LIP = 0x21000006, /* Data Out, No Response, not sent due to a LIP */ + MPI_IOCLOGINFO_FC_TARGET_WRSP_KILLED_BY_LIP = 0x21000007, /* Auto-response after a write not sent due to a LIP */ + MPI_IOCLOGINFO_FC_TARGET_DINR_KILLED_BY_LIP = 0x21000008, /* Data In, No Response, not completed due to a LIP */ + MPI_IOCLOGINFO_FC_TARGET_DINR_MISSING_DATA = 0x21000009, /* Data In, No Response, missing data frames */ + MPI_IOCLOGINFO_FC_TARGET_MRSP_KILLED_BY_LIP = 0x2100000a, /* Manual Response not sent due to a LIP */ + MPI_IOCLOGINFO_FC_TARGET_NO_CLASS_3 = 0x2100000b, /* not sent because remote node does not support Class 3 */ + MPI_IOCLOGINFO_FC_TARGET_LOGIN_NOT_VALID = 0x2100000c, /* not sent because login to remote node not validated */ + MPI_IOCLOGINFO_FC_TARGET_FROM_OUTBOUND = 0x2100000e, /* cleared from the outbound queue after a logout */ + MPI_IOCLOGINFO_FC_TARGET_WAITING_FOR_DATA_IN = 0x2100000f, /* cleared waiting for data after a logout */ + + MPI_IOCLOGINFO_FC_LAN_BASE = 0x22000000, + MPI_IOCLOGINFO_FC_LAN_TRANS_SGL_MISSING = 0x22000001, /* Transaction Context Sgl Missing */ + MPI_IOCLOGINFO_FC_LAN_TRANS_WRONG_PLACE = 0x22000002, /* Transaction Context found before an EOB */ + MPI_IOCLOGINFO_FC_LAN_TRANS_RES_BITS_SET = 0x22000003, /* Transaction Context value has reserved bits set */ + MPI_IOCLOGINFO_FC_LAN_WRONG_SGL_FLAG = 0x22000004, /* Invalid SGL Flags */ + + MPI_IOCLOGINFO_FC_MSG_BASE = 0x23000000, + + MPI_IOCLOGINFO_FC_LINK_BASE = 0x24000000, + MPI_IOCLOGINFO_FC_LINK_LOOP_INIT_TIMEOUT = 0x24000001, /* Loop initialization timed out */ + MPI_IOCLOGINFO_FC_LINK_ALREADY_INITIALIZED = 0x24000002, /* Another system controller already initialized the loop */ + MPI_IOCLOGINFO_FC_LINK_LINK_NOT_ESTABLISHED = 0x24000003, /* Not synchronized to signal or still negotiating (possible cable problem) */ + MPI_IOCLOGINFO_FC_LINK_CRC_ERROR = 0x24000004, /* CRC check detected error on received frame */ + + MPI_IOCLOGINFO_FC_CTX_BASE = 0x25000000, + + MPI_IOCLOGINFO_FC_INVALID_FIELD_BYTE_OFFSET = 0x26000000, /* The lower 24 bits give the byte offset of the field in the request message that is invalid */ + MPI_IOCLOGINFO_FC_INVALID_FIELD_MAX_OFFSET = 0x26ffffff, + + MPI_IOCLOGINFO_FC_STATE_CHANGE = 0x27000000 /* The lower 24 bits give additional information concerning state change */ + +} MpiIocLogInfoFc_t; diff --git a/xen/drivers/message/fusion/lsi/mpi.h b/xen/drivers/message/fusion/lsi/mpi.h new file mode 100644 index 0000000000..0a43905a73 --- /dev/null +++ b/xen/drivers/message/fusion/lsi/mpi.h @@ -0,0 +1,678 @@ +/* + * Copyright (c) 2000-2002 LSI Logic Corporation. + * + * + * Name: MPI.H + * Title: MPI Message independent structures and definitions + * Creation Date: July 27, 2000 + * + * MPI.H Version: 01.02.07 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH definition. + * 06-06-00 01.00.01 Update MPI_VERSION_MAJOR and MPI_VERSION_MINOR. + * 06-22-00 01.00.02 Added MPI_IOCSTATUS_LAN_ definitions. + * Removed LAN_SUSPEND function definition. + * Added MPI_MSGFLAGS_CONTINUATION_REPLY definition. + * 06-30-00 01.00.03 Added MPI_CONTEXT_REPLY_TYPE_LAN definition. + * Added MPI_GET/SET_CONTEXT_REPLY_TYPE macros. + * 07-27-00 01.00.04 Added MPI_FAULT_ definitions. + * Removed MPI_IOCSTATUS_MSG/DATA_XFER_ERROR definitions. + * Added MPI_IOCSTATUS_INTERNAL_ERROR definition. + * Added MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH. + * 11-02-00 01.01.01 Original release for post 1.0 work. + * 12-04-00 01.01.02 Added new function codes. + * 01-09-01 01.01.03 Added more definitions to the system interface section + * Added MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT. + * 01-25-01 01.01.04 Changed MPI_VERSION_MINOR from 0x00 to 0x01. + * 02-20-01 01.01.05 Started using MPI_POINTER. + * Fixed value for MPI_DIAG_RW_ENABLE. + * Added defines for MPI_DIAG_PREVENT_IOC_BOOT and + * MPI_DIAG_CLEAR_FLASH_BAD_SIG. + * Obsoleted MPI_IOCSTATUS_TARGET_FC_ defines. + * 02-27-01 01.01.06 Removed MPI_HOST_INDEX_REGISTER define. + * Added function codes for RAID. + * 04-09-01 01.01.07 Added alternate define for MPI_DOORBELL_ACTIVE, + * MPI_DOORBELL_USED, to better match the spec. + * 08-08-01 01.02.01 Original release for v1.2 work. + * Changed MPI_VERSION_MINOR from 0x01 to 0x02. + * Added define MPI_FUNCTION_TOOLBOX. + * 09-28-01 01.02.02 New function code MPI_SCSI_ENCLOSURE_PROCESSOR. + * 11-01-01 01.02.03 Changed name to MPI_FUNCTION_SCSI_ENCLOSURE_PROCESSOR. + * 03-14-02 01.02.04 Added MPI_HEADER_VERSION_ defines. + * 05-31-02 01.02.05 Bumped MPI_HEADER_VERSION_UNIT. + * 07-12-02 01.02.06 Added define for MPI_FUNCTION_MAILBOX. + * 09-16-02 01.02.07 Bumped value for MPI_HEADER_VERSION_UNIT. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_H +#define MPI_H + + +/***************************************************************************** +* +* M P I V e r s i o n D e f i n i t i o n s +* +*****************************************************************************/ + +#define MPI_VERSION_MAJOR (0x01) +#define MPI_VERSION_MINOR (0x02) +#define MPI_VERSION_MAJOR_MASK (0xFF00) +#define MPI_VERSION_MAJOR_SHIFT (8) +#define MPI_VERSION_MINOR_MASK (0x00FF) +#define MPI_VERSION_MINOR_SHIFT (0) +#define MPI_VERSION ((MPI_VERSION_MAJOR << MPI_VERSION_MAJOR_SHIFT) | \ + MPI_VERSION_MINOR) + +#define MPI_VERSION_01_00 (0x0100) +#define MPI_VERSION_01_01 (0x0101) +#define MPI_VERSION_01_02 (0x0102) +/* Note: The major versions of 0xe0 through 0xff are reserved */ + +/* versioning for this MPI header set */ +#define MPI_HEADER_VERSION_UNIT (0x09) +#define MPI_HEADER_VERSION_DEV (0x00) +#define MPI_HEADER_VERSION_UNIT_MASK (0xFF00) +#define MPI_HEADER_VERSION_UNIT_SHIFT (8) +#define MPI_HEADER_VERSION_DEV_MASK (0x00FF) +#define MPI_HEADER_VERSION_DEV_SHIFT (0) +#define MPI_HEADER_VERSION ((MPI_HEADER_VERSION_UNIT << 8) | MPI_HEADER_VERSION_DEV) + +/***************************************************************************** +* +* I O C S t a t e D e f i n i t i o n s +* +*****************************************************************************/ + +#define MPI_IOC_STATE_RESET (0x00000000) +#define MPI_IOC_STATE_READY (0x10000000) +#define MPI_IOC_STATE_OPERATIONAL (0x20000000) +#define MPI_IOC_STATE_FAULT (0x40000000) + +#define MPI_IOC_STATE_MASK (0xF0000000) +#define MPI_IOC_STATE_SHIFT (28) + +/* Fault state codes (product independent range 0x8000-0xFFFF) */ + +#define MPI_FAULT_REQUEST_MESSAGE_PCI_PARITY_ERROR (0x8111) +#define MPI_FAULT_REQUEST_MESSAGE_PCI_BUS_FAULT (0x8112) +#define MPI_FAULT_REPLY_MESSAGE_PCI_PARITY_ERROR (0x8113) +#define MPI_FAULT_REPLY_MESSAGE_PCI_BUS_FAULT (0x8114) +#define MPI_FAULT_DATA_SEND_PCI_PARITY_ERROR (0x8115) +#define MPI_FAULT_DATA_SEND_PCI_BUS_FAULT (0x8116) +#define MPI_FAULT_DATA_RECEIVE_PCI_PARITY_ERROR (0x8117) +#define MPI_FAULT_DATA_RECEIVE_PCI_BUS_FAULT (0x8118) + + +/***************************************************************************** +* +* P C I S y s t e m I n t e r f a c e R e g i s t e r s +* +*****************************************************************************/ + +/* S y s t e m D o o r b e l l */ +#define MPI_DOORBELL_OFFSET (0x00000000) +#define MPI_DOORBELL_ACTIVE (0x08000000) /* DoorbellUsed */ +#define MPI_DOORBELL_USED (MPI_DOORBELL_ACTIVE) +#define MPI_DOORBELL_ACTIVE_SHIFT (27) +#define MPI_DOORBELL_WHO_INIT_MASK (0x07000000) +#define MPI_DOORBELL_WHO_INIT_SHIFT (24) +#define MPI_DOORBELL_FUNCTION_MASK (0xFF000000) +#define MPI_DOORBELL_FUNCTION_SHIFT (24) +#define MPI_DOORBELL_ADD_DWORDS_MASK (0x00FF0000) +#define MPI_DOORBELL_ADD_DWORDS_SHIFT (16) +#define MPI_DOORBELL_DATA_MASK (0x0000FFFF) + + +#define MPI_WRITE_SEQUENCE_OFFSET (0x00000004) +#define MPI_WRSEQ_KEY_VALUE_MASK (0x0000000F) +#define MPI_WRSEQ_1ST_KEY_VALUE (0x04) +#define MPI_WRSEQ_2ND_KEY_VALUE (0x0B) +#define MPI_WRSEQ_3RD_KEY_VALUE (0x02) +#define MPI_WRSEQ_4TH_KEY_VALUE (0x07) +#define MPI_WRSEQ_5TH_KEY_VALUE (0x0D) + +#define MPI_DIAGNOSTIC_OFFSET (0x00000008) +#define MPI_DIAG_CLEAR_FLASH_BAD_SIG (0x00000400) +#define MPI_DIAG_PREVENT_IOC_BOOT (0x00000200) +#define MPI_DIAG_DRWE (0x00000080) +#define MPI_DIAG_FLASH_BAD_SIG (0x00000040) +#define MPI_DIAG_RESET_HISTORY (0x00000020) +#define MPI_DIAG_RW_ENABLE (0x00000010) +#define MPI_DIAG_RESET_ADAPTER (0x00000004) +#define MPI_DIAG_DISABLE_ARM (0x00000002) +#define MPI_DIAG_MEM_ENABLE (0x00000001) + +#define MPI_TEST_BASE_ADDRESS_OFFSET (0x0000000C) + +#define MPI_DIAG_RW_DATA_OFFSET (0x00000010) + +#define MPI_DIAG_RW_ADDRESS_OFFSET (0x00000014) + +#define MPI_HOST_INTERRUPT_STATUS_OFFSET (0x00000030) +#define MPI_HIS_IOP_DOORBELL_STATUS (0x80000000) +#define MPI_HIS_REPLY_MESSAGE_INTERRUPT (0x00000008) +#define MPI_HIS_DOORBELL_INTERRUPT (0x00000001) + +#define MPI_HOST_INTERRUPT_MASK_OFFSET (0x00000034) +#define MPI_HIM_RIM (0x00000008) +#define MPI_HIM_DIM (0x00000001) + +#define MPI_REQUEST_QUEUE_OFFSET (0x00000040) +#define MPI_REQUEST_POST_FIFO_OFFSET (0x00000040) + +#define MPI_REPLY_QUEUE_OFFSET (0x00000044) +#define MPI_REPLY_POST_FIFO_OFFSET (0x00000044) +#define MPI_REPLY_FREE_FIFO_OFFSET (0x00000044) + + + +/***************************************************************************** +* +* M e s s a g e F r a m e D e s c r i p t o r s +* +*****************************************************************************/ + +#define MPI_REQ_MF_DESCRIPTOR_NB_MASK (0x00000003) +#define MPI_REQ_MF_DESCRIPTOR_F_BIT (0x00000004) +#define MPI_REQ_MF_DESCRIPTOR_ADDRESS_MASK (0xFFFFFFF8) + +#define MPI_ADDRESS_REPLY_A_BIT (0x80000000) +#define MPI_ADDRESS_REPLY_ADDRESS_MASK (0x7FFFFFFF) + +#define MPI_CONTEXT_REPLY_A_BIT (0x80000000) +#define MPI_CONTEXT_REPLY_TYPE_MASK (0x60000000) +#define MPI_CONTEXT_REPLY_TYPE_SCSI_INIT (0x00) +#define MPI_CONTEXT_REPLY_TYPE_SCSI_TARGET (0x01) +#define MPI_CONTEXT_REPLY_TYPE_LAN (0x02) +#define MPI_CONTEXT_REPLY_TYPE_SHIFT (29) +#define MPI_CONTEXT_REPLY_CONTEXT_MASK (0x1FFFFFFF) + + +/****************************************************************************/ +/* Context Reply macros */ +/****************************************************************************/ + +#define MPI_GET_CONTEXT_REPLY_TYPE(x) (((x) & MPI_CONTEXT_REPLY_TYPE_MASK) \ + >> MPI_CONTEXT_REPLY_TYPE_SHIFT) + +#define MPI_SET_CONTEXT_REPLY_TYPE(x, typ) \ + ((x) = ((x) & ~MPI_CONTEXT_REPLY_TYPE_MASK) | \ + (((typ) << MPI_CONTEXT_REPLY_TYPE_SHIFT) & \ + MPI_CONTEXT_REPLY_TYPE_MASK)) + + +/***************************************************************************** +* +* M e s s a g e F u n c t i o n s +* 0x80 -> 0x8F reserved for private message use per product +* +* +*****************************************************************************/ + +#define MPI_FUNCTION_SCSI_IO_REQUEST (0x00) +#define MPI_FUNCTION_SCSI_TASK_MGMT (0x01) +#define MPI_FUNCTION_IOC_INIT (0x02) +#define MPI_FUNCTION_IOC_FACTS (0x03) +#define MPI_FUNCTION_CONFIG (0x04) +#define MPI_FUNCTION_PORT_FACTS (0x05) +#define MPI_FUNCTION_PORT_ENABLE (0x06) +#define MPI_FUNCTION_EVENT_NOTIFICATION (0x07) +#define MPI_FUNCTION_EVENT_ACK (0x08) +#define MPI_FUNCTION_FW_DOWNLOAD (0x09) +#define MPI_FUNCTION_TARGET_CMD_BUFFER_POST (0x0A) +#define MPI_FUNCTION_TARGET_ASSIST (0x0B) +#define MPI_FUNCTION_TARGET_STATUS_SEND (0x0C) +#define MPI_FUNCTION_TARGET_MODE_ABORT (0x0D) +#define MPI_FUNCTION_TARGET_FC_BUF_POST_LINK_SRVC (0x0E) /* obsolete name */ +#define MPI_FUNCTION_TARGET_FC_RSP_LINK_SRVC (0x0F) /* obsolete name */ +#define MPI_FUNCTION_TARGET_FC_EX_SEND_LINK_SRVC (0x10) /* obsolete name */ +#define MPI_FUNCTION_TARGET_FC_ABORT (0x11) /* obsolete name */ +#define MPI_FUNCTION_FC_LINK_SRVC_BUF_POST (0x0E) +#define MPI_FUNCTION_FC_LINK_SRVC_RSP (0x0F) +#define MPI_FUNCTION_FC_EX_LINK_SRVC_SEND (0x10) +#define MPI_FUNCTION_FC_ABORT (0x11) +#define MPI_FUNCTION_FW_UPLOAD (0x12) +#define MPI_FUNCTION_FC_COMMON_TRANSPORT_SEND (0x13) +#define MPI_FUNCTION_FC_PRIMITIVE_SEND (0x14) + +#define MPI_FUNCTION_RAID_ACTION (0x15) +#define MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH (0x16) + +#define MPI_FUNCTION_TOOLBOX (0x17) + +#define MPI_FUNCTION_SCSI_ENCLOSURE_PROCESSOR (0x18) + +#define MPI_FUNCTION_MAILBOX (0x19) + +#define MPI_FUNCTION_LAN_SEND (0x20) +#define MPI_FUNCTION_LAN_RECEIVE (0x21) +#define MPI_FUNCTION_LAN_RESET (0x22) + +#define MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET (0x40) +#define MPI_FUNCTION_IO_UNIT_RESET (0x41) +#define MPI_FUNCTION_HANDSHAKE (0x42) +#define MPI_FUNCTION_REPLY_FRAME_REMOVAL (0x43) + + + +/***************************************************************************** +* +* S c a t t e r G a t h e r E l e m e n t s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Simple element structures */ +/****************************************************************************/ + +typedef struct _SGE_SIMPLE32 +{ + U32 FlagsLength; + U32 Address; +} SGE_SIMPLE32, MPI_POINTER PTR_SGE_SIMPLE32, + SGESimple32_t, MPI_POINTER pSGESimple32_t; + +typedef struct _SGE_SIMPLE64 +{ + U32 FlagsLength; + U64 Address; +} SGE_SIMPLE64, MPI_POINTER PTR_SGE_SIMPLE64, + SGESimple64_t, MPI_POINTER pSGESimple64_t; + +typedef struct _SGE_SIMPLE_UNION +{ + U32 FlagsLength; + union + { + U32 Address32; + U64 Address64; + }u; +} SGESimpleUnion_t, MPI_POINTER pSGESimpleUnion_t, + SGE_SIMPLE_UNION, MPI_POINTER PTR_SGE_SIMPLE_UNION; + +/****************************************************************************/ +/* Chain element structures */ +/****************************************************************************/ + +typedef struct _SGE_CHAIN32 +{ + U16 Length; + U8 NextChainOffset; + U8 Flags; + U32 Address; +} SGE_CHAIN32, MPI_POINTER PTR_SGE_CHAIN32, + SGEChain32_t, MPI_POINTER pSGEChain32_t; + +typedef struct _SGE_CHAIN64 +{ + U16 Length; + U8 NextChainOffset; + U8 Flags; + U64 Address; +} SGE_CHAIN64, MPI_POINTER PTR_SGE_CHAIN64, + SGEChain64_t, MPI_POINTER pSGEChain64_t; + +typedef struct _SGE_CHAIN_UNION +{ + U16 Length; + U8 NextChainOffset; + U8 Flags; + union + { + U32 Address32; + U64 Address64; + }u; +} SGE_CHAIN_UNION, MPI_POINTER PTR_SGE_CHAIN_UNION, + SGEChainUnion_t, MPI_POINTER pSGEChainUnion_t; + +/****************************************************************************/ +/* Transaction Context element */ +/****************************************************************************/ + +typedef struct _SGE_TRANSACTION32 +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + U32 TransactionContext[1]; + U32 TransactionDetails[1]; +} SGE_TRANSACTION32, MPI_POINTER PTR_SGE_TRANSACTION32, + SGETransaction32_t, MPI_POINTER pSGETransaction32_t; + +typedef struct _SGE_TRANSACTION64 +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + U32 TransactionContext[2]; + U32 TransactionDetails[1]; +} SGE_TRANSACTION64, MPI_POINTER PTR_SGE_TRANSACTION64, + SGETransaction64_t, MPI_POINTER pSGETransaction64_t; + +typedef struct _SGE_TRANSACTION96 +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + U32 TransactionContext[3]; + U32 TransactionDetails[1]; +} SGE_TRANSACTION96, MPI_POINTER PTR_SGE_TRANSACTION96, + SGETransaction96_t, MPI_POINTER pSGETransaction96_t; + +typedef struct _SGE_TRANSACTION128 +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + U32 TransactionContext[4]; + U32 TransactionDetails[1]; +} SGE_TRANSACTION128, MPI_POINTER PTR_SGE_TRANSACTION128, + SGETransaction_t128, MPI_POINTER pSGETransaction_t128; + +typedef struct _SGE_TRANSACTION_UNION +{ + U8 Reserved; + U8 ContextSize; + U8 DetailsLength; + U8 Flags; + union + { + U32 TransactionContext32[1]; + U32 TransactionContext64[2]; + U32 TransactionContext96[3]; + U32 TransactionContext128[4]; + }u; + U32 TransactionDetails[1]; +} SGE_TRANSACTION_UNION, MPI_POINTER PTR_SGE_TRANSACTION_UNION, + SGETransactionUnion_t, MPI_POINTER pSGETransactionUnion_t; + + +/****************************************************************************/ +/* SGE IO types union for IO SGL's */ +/****************************************************************************/ + +typedef struct _SGE_IO_UNION +{ + union + { + SGE_SIMPLE_UNION Simple; + SGE_CHAIN_UNION Chain; + } u; +} SGE_IO_UNION, MPI_POINTER PTR_SGE_IO_UNION, + SGEIOUnion_t, MPI_POINTER pSGEIOUnion_t; + +/****************************************************************************/ +/* SGE union for SGL's with Simple and Transaction elements */ +/****************************************************************************/ + +typedef struct _SGE_TRANS_SIMPLE_UNION +{ + union + { + SGE_SIMPLE_UNION Simple; + SGE_TRANSACTION_UNION Transaction; + } u; +} SGE_TRANS_SIMPLE_UNION, MPI_POINTER PTR_SGE_TRANS_SIMPLE_UNION, + SGETransSimpleUnion_t, MPI_POINTER pSGETransSimpleUnion_t; + +/****************************************************************************/ +/* All SGE types union */ +/****************************************************************************/ + +typedef struct _SGE_MPI_UNION +{ + union + { + SGE_SIMPLE_UNION Simple; + SGE_CHAIN_UNION Chain; + SGE_TRANSACTION_UNION Transaction; + } u; +} SGE_MPI_UNION, MPI_POINTER PTR_SGE_MPI_UNION, + MPI_SGE_UNION_t, MPI_POINTER pMPI_SGE_UNION_t, + SGEAllUnion_t, MPI_POINTER pSGEAllUnion_t; + + +/****************************************************************************/ +/* SGE field definition and masks */ +/****************************************************************************/ + +/* Flags field bit definitions */ + +#define MPI_SGE_FLAGS_LAST_ELEMENT (0x80) +#define MPI_SGE_FLAGS_END_OF_BUFFER (0x40) +#define MPI_SGE_FLAGS_ELEMENT_TYPE_MASK (0x30) +#define MPI_SGE_FLAGS_LOCAL_ADDRESS (0x08) +#define MPI_SGE_FLAGS_DIRECTION (0x04) +#define MPI_SGE_FLAGS_ADDRESS_SIZE (0x02) +#define MPI_SGE_FLAGS_END_OF_LIST (0x01) + +#define MPI_SGE_FLAGS_SHIFT (24) + +#define MPI_SGE_LENGTH_MASK (0x00FFFFFF) +#define MPI_SGE_CHAIN_LENGTH_MASK (0x0000FFFF) + +/* Element Type */ + +#define MPI_SGE_FLAGS_TRANSACTION_ELEMENT (0x00) +#define MPI_SGE_FLAGS_SIMPLE_ELEMENT (0x10) +#define MPI_SGE_FLAGS_CHAIN_ELEMENT (0x30) +#define MPI_SGE_FLAGS_ELEMENT_MASK (0x30) + +/* Address location */ + +#define MPI_SGE_FLAGS_SYSTEM_ADDRESS (0x00) + +/* Direction */ + +#define MPI_SGE_FLAGS_IOC_TO_HOST (0x00) +#define MPI_SGE_FLAGS_HOST_TO_IOC (0x04) + +/* Address Size */ + +#define MPI_SGE_FLAGS_32_BIT_ADDRESSING (0x00) +#define MPI_SGE_FLAGS_64_BIT_ADDRESSING (0x02) + +/* Context Size */ + +#define MPI_SGE_FLAGS_32_BIT_CONTEXT (0x00) +#define MPI_SGE_FLAGS_64_BIT_CONTEXT (0x02) +#define MPI_SGE_FLAGS_96_BIT_CONTEXT (0x04) +#define MPI_SGE_FLAGS_128_BIT_CONTEXT (0x06) + +#define MPI_SGE_CHAIN_OFFSET_MASK (0x00FF0000) +#define MPI_SGE_CHAIN_OFFSET_SHIFT (16) + + +/****************************************************************************/ +/* SGE operation Macros */ +/****************************************************************************/ + + /* SIMPLE FlagsLength manipulations... */ +#define MPI_SGE_SET_FLAGS(f) ((U32)(f) << MPI_SGE_FLAGS_SHIFT) +#define MPI_SGE_GET_FLAGS(fl) (((fl) & ~MPI_SGE_LENGTH_MASK) >> MPI_SGE_FLAGS_SHIFT) +#define MPI_SGE_LENGTH(fl) ((fl) & MPI_SGE_LENGTH_MASK) +#define MPI_SGE_CHAIN_LENGTH(fl) ((fl) & MPI_SGE_CHAIN_LENGTH_MASK) + +#define MPI_SGE_SET_FLAGS_LENGTH(f,l) (MPI_SGE_SET_FLAGS(f) | MPI_SGE_LENGTH(l)) + +#define MPI_pSGE_GET_FLAGS(psg) MPI_SGE_GET_FLAGS((psg)->FlagsLength) +#define MPI_pSGE_GET_LENGTH(psg) MPI_SGE_LENGTH((psg)->FlagsLength) +#define MPI_pSGE_SET_FLAGS_LENGTH(psg,f,l) (psg)->FlagsLength = MPI_SGE_SET_FLAGS_LENGTH(f,l) + /* CAUTION - The following are READ-MODIFY-WRITE! */ +#define MPI_pSGE_SET_FLAGS(psg,f) (psg)->FlagsLength |= MPI_SGE_SET_FLAGS(f) +#define MPI_pSGE_SET_LENGTH(psg,l) (psg)->FlagsLength |= MPI_SGE_LENGTH(l) + +#define MPI_GET_CHAIN_OFFSET(x) ((x&MPI_SGE_CHAIN_OFFSET_MASK)>>MPI_SGE_CHAIN_OFFSET_SHIFT) + + + +/***************************************************************************** +* +* S t a n d a r d M e s s a g e S t r u c t u r e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Standard message request header for all request messages */ +/****************************************************************************/ + +typedef struct _MSG_REQUEST_HEADER +{ + U8 Reserved[2]; /* function specific */ + U8 ChainOffset; + U8 Function; + U8 Reserved1[3]; /* function specific */ + U8 MsgFlags; + U32 MsgContext; +} MSG_REQUEST_HEADER, MPI_POINTER PTR_MSG_REQUEST_HEADER, + MPIHeader_t, MPI_POINTER pMPIHeader_t; + + +/****************************************************************************/ +/* Default Reply */ +/****************************************************************************/ + +typedef struct _MSG_DEFAULT_REPLY +{ + U8 Reserved[2]; /* function specific */ + U8 MsgLength; + U8 Function; + U8 Reserved1[3]; /* function specific */ + U8 MsgFlags; + U32 MsgContext; + U8 Reserved2[2]; /* function specific */ + U16 IOCStatus; + U32 IOCLogInfo; +} MSG_DEFAULT_REPLY, MPI_POINTER PTR_MSG_DEFAULT_REPLY, + MPIDefaultReply_t, MPI_POINTER pMPIDefaultReply_t; + + +/* MsgFlags definition for all replies */ + +#define MPI_MSGFLAGS_CONTINUATION_REPLY (0x80) + + +/***************************************************************************** +* +* I O C S t a t u s V a l u e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Common IOCStatus values for all replies */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_SUCCESS (0x0000) +#define MPI_IOCSTATUS_INVALID_FUNCTION (0x0001) +#define MPI_IOCSTATUS_BUSY (0x0002) +#define MPI_IOCSTATUS_INVALID_SGL (0x0003) +#define MPI_IOCSTATUS_INTERNAL_ERROR (0x0004) +#define MPI_IOCSTATUS_RESERVED (0x0005) +#define MPI_IOCSTATUS_INSUFFICIENT_RESOURCES (0x0006) +#define MPI_IOCSTATUS_INVALID_FIELD (0x0007) +#define MPI_IOCSTATUS_INVALID_STATE (0x0008) + +/****************************************************************************/ +/* Config IOCStatus values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_CONFIG_INVALID_ACTION (0x0020) +#define MPI_IOCSTATUS_CONFIG_INVALID_TYPE (0x0021) +#define MPI_IOCSTATUS_CONFIG_INVALID_PAGE (0x0022) +#define MPI_IOCSTATUS_CONFIG_INVALID_DATA (0x0023) +#define MPI_IOCSTATUS_CONFIG_NO_DEFAULTS (0x0024) +#define MPI_IOCSTATUS_CONFIG_CANT_COMMIT (0x0025) + +/****************************************************************************/ +/* SCSIIO Reply (SPI & FCP) initiator values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_SCSI_RECOVERED_ERROR (0x0040) +#define MPI_IOCSTATUS_SCSI_INVALID_BUS (0x0041) +#define MPI_IOCSTATUS_SCSI_INVALID_TARGETID (0x0042) +#define MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE (0x0043) +#define MPI_IOCSTATUS_SCSI_DATA_OVERRUN (0x0044) +#define MPI_IOCSTATUS_SCSI_DATA_UNDERRUN (0x0045) +#define MPI_IOCSTATUS_SCSI_IO_DATA_ERROR (0x0046) +#define MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR (0x0047) +#define MPI_IOCSTATUS_SCSI_TASK_TERMINATED (0x0048) +#define MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH (0x0049) +#define MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED (0x004A) +#define MPI_IOCSTATUS_SCSI_IOC_TERMINATED (0x004B) +#define MPI_IOCSTATUS_SCSI_EXT_TERMINATED (0x004C) + +/****************************************************************************/ +/* SCSI (SPI & FCP) target values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_TARGET_PRIORITY_IO (0x0060) +#define MPI_IOCSTATUS_TARGET_INVALID_PORT (0x0061) +#define MPI_IOCSTATUS_TARGET_INVALID_IOCINDEX (0x0062) +#define MPI_IOCSTATUS_TARGET_ABORTED (0x0063) +#define MPI_IOCSTATUS_TARGET_NO_CONN_RETRYABLE (0x0064) +#define MPI_IOCSTATUS_TARGET_NO_CONNECTION (0x0065) +#define MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH (0x006A) +#define MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT (0x006B) + +/****************************************************************************/ +/* Additional FCP target values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_TARGET_FC_ABORTED (0x0066) /* obsolete */ +#define MPI_IOCSTATUS_TARGET_FC_RX_ID_INVALID (0x0067) /* obsolete */ +#define MPI_IOCSTATUS_TARGET_FC_DID_INVALID (0x0068) /* obsolete */ +#define MPI_IOCSTATUS_TARGET_FC_NODE_LOGGED_OUT (0x0069) /* obsolete */ + +/****************************************************************************/ +/* Fibre Channel Direct Access values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_FC_ABORTED (0x0066) +#define MPI_IOCSTATUS_FC_RX_ID_INVALID (0x0067) +#define MPI_IOCSTATUS_FC_DID_INVALID (0x0068) +#define MPI_IOCSTATUS_FC_NODE_LOGGED_OUT (0x0069) + +/****************************************************************************/ +/* LAN values */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_LAN_DEVICE_NOT_FOUND (0x0080) +#define MPI_IOCSTATUS_LAN_DEVICE_FAILURE (0x0081) +#define MPI_IOCSTATUS_LAN_TRANSMIT_ERROR (0x0082) +#define MPI_IOCSTATUS_LAN_TRANSMIT_ABORTED (0x0083) +#define MPI_IOCSTATUS_LAN_RECEIVE_ERROR (0x0084) +#define MPI_IOCSTATUS_LAN_RECEIVE_ABORTED (0x0085) +#define MPI_IOCSTATUS_LAN_PARTIAL_PACKET (0x0086) +#define MPI_IOCSTATUS_LAN_CANCELED (0x0087) + + +/****************************************************************************/ +/* IOCStatus flag to indicate that log info is available */ +/****************************************************************************/ + +#define MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE (0x8000) +#define MPI_IOCSTATUS_MASK (0x7FFF) + +/****************************************************************************/ +/* LogInfo Types */ +/****************************************************************************/ + +#define MPI_IOCLOGINFO_TYPE_MASK (0xF0000000) +#define MPI_IOCLOGINFO_TYPE_NONE (0x0) +#define MPI_IOCLOGINFO_TYPE_SCSI (0x1) +#define MPI_IOCLOGINFO_TYPE_FC (0x2) +#define MPI_IOCLOGINFO_LOG_DATA_MASK (0x0FFFFFFF) + + +#endif diff --git a/xen/drivers/message/fusion/lsi/mpi_cnfg.h b/xen/drivers/message/fusion/lsi/mpi_cnfg.h new file mode 100644 index 0000000000..47a5afcdc2 --- /dev/null +++ b/xen/drivers/message/fusion/lsi/mpi_cnfg.h @@ -0,0 +1,1569 @@ +/* + * Copyright (c) 2000-2002 LSI Logic Corporation. + * + * + * Name: MPI_CNFG.H + * Title: MPI Config message, structures, and Pages + * Creation Date: July 27, 2000 + * + * MPI_CNFG.H Version: 01.02.09 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-08-00 01.00.02 Added _PAGEVERSION definitions for all pages. + * Added FcPhLowestVersion, FcPhHighestVersion, Reserved2 + * fields to FC_DEVICE_0 page, updated the page version. + * Changed _FREE_RUNNING_CLOCK to _PACING_TRANSFERS in + * SCSI_PORT_0, SCSI_DEVICE_0 and SCSI_DEVICE_1 pages + * and updated the page versions. + * Added _RESPONSE_ID_MASK definition to SCSI_PORT_1 + * page and updated the page version. + * Added Information field and _INFO_PARAMS_NEGOTIATED + * definitionto SCSI_DEVICE_0 page. + * 06-22-00 01.00.03 Removed batch controls from LAN_0 page and updated the + * page version. + * Added BucketsRemaining to LAN_1 page, redefined the + * state values, and updated the page version. + * Revised bus width definitions in SCSI_PORT_0, + * SCSI_DEVICE_0 and SCSI_DEVICE_1 pages. + * 06-30-00 01.00.04 Added MaxReplySize to LAN_1 page and updated the page + * version. + * Moved FC_DEVICE_0 PageAddress description to spec. + * 07-27-00 01.00.05 Corrected the SubsystemVendorID and SubsystemID field + * widths in IOC_0 page and updated the page version. + * 11-02-00 01.01.01 Original release for post 1.0 work + * Added Manufacturing pages, IO Unit Page 2, SCSI SPI + * Port Page 2, FC Port Page 4, FC Port Page 5 + * 11-15-00 01.01.02 Interim changes to match proposals + * 12-04-00 01.01.03 Config page changes to match MPI rev 1.00.01. + * 12-05-00 01.01.04 Modified config page actions. + * 01-09-01 01.01.05 Added defines for page address formats. + * Data size for Manufacturing pages 2 and 3 no longer + * defined here. + * Io Unit Page 2 size is fixed at 4 adapters and some + * flags were changed. + * SCSI Port Page 2 Device Settings modified. + * New fields added to FC Port Page 0 and some flags + * cleaned up. + * Removed impedance flash from FC Port Page 1. + * Added FC Port pages 6 and 7. + * 01-25-01 01.01.06 Added MaxInitiators field to FcPortPage0. + * 01-29-01 01.01.07 Changed some defines to make them 32 character unique. + * Added some LinkType defines for FcPortPage0. + * 02-20-01 01.01.08 Started using MPI_POINTER. + * 02-27-01 01.01.09 Replaced MPI_CONFIG_PAGETYPE_SCSI_LUN with + * MPI_CONFIG_PAGETYPE_RAID_VOLUME. + * Added definitions and structures for IOC Page 2 and + * RAID Volume Page 2. + * 03-27-01 01.01.10 Added CONFIG_PAGE_FC_PORT_8 and CONFIG_PAGE_FC_PORT_9. + * CONFIG_PAGE_FC_PORT_3 now supports persistent by DID. + * Added VendorId and ProductRevLevel fields to + * RAIDVOL2_IM_PHYS_ID struct. + * Modified values for MPI_FCPORTPAGE0_FLAGS_ATTACH_ + * defines to make them compatible to MPI version 1.0. + * Added structure offset comments. + * 04-09-01 01.01.11 Added some new defines for the PageAddress field and + * removed some obsolete ones. + * Added IO Unit Page 3. + * Modified defines for Scsi Port Page 2. + * Modified RAID Volume Pages. + * 08-08-01 01.02.01 Original release for v1.2 work. + * Added SepID and SepBus to RVP2 IMPhysicalDisk struct. + * Added defines for the SEP bits in RVP2 VolumeSettings. + * Modified the DeviceSettings field in RVP2 to use the + * proper structure. + * Added defines for SES, SAF-TE, and cross channel for + * IOCPage2 CapabilitiesFlags. + * Removed define for MPI_IOUNITPAGE2_FLAGS_RAID_DISABLE. + * Removed define for + * MPI_SCSIPORTPAGE2_PORT_FLAGS_PARITY_ENABLE. + * Added define for MPI_CONFIG_PAGEATTR_RO_PERSISTENT. + * 08-29-01 01.02.02 Fixed value for MPI_MANUFACTPAGE_DEVID_53C1035. + * Added defines for MPI_FCPORTPAGE1_FLAGS_HARD_ALPA_ONLY + * and MPI_FCPORTPAGE1_FLAGS_IMMEDIATE_ERROR_REPLY. + * Removed MPI_SCSIPORTPAGE0_CAP_PACING_TRANSFERS, + * MPI_SCSIDEVPAGE0_NP_PACING_TRANSFERS, and + * MPI_SCSIDEVPAGE1_RP_PACING_TRANSFERS, and + * MPI_SCSIDEVPAGE1_CONF_PPR_ALLOWED. + * Added defines for MPI_SCSIDEVPAGE1_CONF_WDTR_DISALLOWED + * and MPI_SCSIDEVPAGE1_CONF_SDTR_DISALLOWED. + * Added OnBusTimerValue to CONFIG_PAGE_SCSI_PORT_1. + * Added rejected bits to SCSI Device Page 0 Information. + * Increased size of ALPA array in FC Port Page 2 by one + * and removed a one byte reserved field. + * 09-28-01 01.02.03 Swapped NegWireSpeedLow and NegWireSpeedLow in + * CONFIG_PAGE_LAN_1 to match preferred 64-bit ordering. + * Added structures for Manufacturing Page 4, IO Unit + * Page 3, IOC Page 3, IOC Page 4, RAID Volume Page 0, and + * RAID PhysDisk Page 0. + * 10-04-01 01.02.04 Added define for MPI_CONFIG_PAGETYPE_RAID_PHYSDISK. + * Modified some of the new defines to make them 32 + * character unique. + * Modified how variable length pages (arrays) are defined. + * Added generic defines for hot spare pools and RAID + * volume types. + * 11-01-01 01.02.05 Added define for MPI_IOUNITPAGE1_DISABLE_IR. + * 03-14-02 01.02.06 Added PCISlotNum field to CONFIG_PAGE_IOC_1 along with + * related define, and bumped the page version define. + * 05-31-02 01.02.07 Added a Flags field to CONFIG_PAGE_IOC_2_RAID_VOL in a + * reserved byte and added a define. + * Added define for + * MPI_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE. + * Added new config page: CONFIG_PAGE_IOC_5. + * Added MaxAliases, MaxHardAliases, and NumCurrentAliases + * fields to CONFIG_PAGE_FC_PORT_0. + * Added AltConnector and NumRequestedAliases fields to + * CONFIG_PAGE_FC_PORT_1. + * Added new config page: CONFIG_PAGE_FC_PORT_10. + * 07-12-02 01.02.08 Added more MPI_MANUFACTPAGE_DEVID_ defines. + * Added additional MPI_SCSIDEVPAGE0_NP_ defines. + * Added more MPI_SCSIDEVPAGE1_RP_ defines. + * Added define for + * MPI_SCSIDEVPAGE1_CONF_EXTENDED_PARAMS_ENABLE. + * Added new config page: CONFIG_PAGE_SCSI_DEVICE_3. + * Modified MPI_FCPORTPAGE5_FLAGS_ defines. + * 09-16-02 01.02.09 Added more MPI_SCSIDEVPAGE1_CONF_FORCE_PPR_MSG define. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_CNFG_H +#define MPI_CNFG_H + + +/***************************************************************************** +* +* C o n f i g M e s s a g e a n d S t r u c t u r e s +* +*****************************************************************************/ + +typedef struct _CONFIG_PAGE_HEADER +{ + U8 PageVersion; /* 00h */ + U8 PageLength; /* 01h */ + U8 PageNumber; /* 02h */ + U8 PageType; /* 03h */ +} fCONFIG_PAGE_HEADER, MPI_POINTER PTR_CONFIG_PAGE_HEADER, + ConfigPageHeader_t, MPI_POINTER pConfigPageHeader_t; + +typedef union _CONFIG_PAGE_HEADER_UNION +{ + ConfigPageHeader_t Struct; + U8 Bytes[4]; + U16 Word16[2]; + U32 Word32; +} ConfigPageHeaderUnion, MPI_POINTER pConfigPageHeaderUnion, + fCONFIG_PAGE_HEADER_UNION, MPI_POINTER PTR_CONFIG_PAGE_HEADER_UNION; + + +/**************************************************************************** +* PageType field values +****************************************************************************/ +#define MPI_CONFIG_PAGEATTR_READ_ONLY (0x00) +#define MPI_CONFIG_PAGEATTR_CHANGEABLE (0x10) +#define MPI_CONFIG_PAGEATTR_PERSISTENT (0x20) +#define MPI_CONFIG_PAGEATTR_RO_PERSISTENT (0x30) +#define MPI_CONFIG_PAGEATTR_MASK (0xF0) + +#define MPI_CONFIG_PAGETYPE_IO_UNIT (0x00) +#define MPI_CONFIG_PAGETYPE_IOC (0x01) +#define MPI_CONFIG_PAGETYPE_BIOS (0x02) +#define MPI_CONFIG_PAGETYPE_SCSI_PORT (0x03) +#define MPI_CONFIG_PAGETYPE_SCSI_DEVICE (0x04) +#define MPI_CONFIG_PAGETYPE_FC_PORT (0x05) +#define MPI_CONFIG_PAGETYPE_FC_DEVICE (0x06) +#define MPI_CONFIG_PAGETYPE_LAN (0x07) +#define MPI_CONFIG_PAGETYPE_RAID_VOLUME (0x08) +#define MPI_CONFIG_PAGETYPE_MANUFACTURING (0x09) +#define MPI_CONFIG_PAGETYPE_RAID_PHYSDISK (0x0A) +#define MPI_CONFIG_PAGETYPE_MASK (0x0F) + +#define MPI_CONFIG_TYPENUM_MASK (0x0FFF) + + +/**************************************************************************** +* PageAddress field values +****************************************************************************/ +#define MPI_SCSI_PORT_PGAD_PORT_MASK (0x000000FF) + +#define MPI_SCSI_DEVICE_TARGET_ID_MASK (0x000000FF) +#define MPI_SCSI_DEVICE_TARGET_ID_SHIFT (0) +#define MPI_SCSI_DEVICE_BUS_MASK (0x0000FF00) +#define MPI_SCSI_DEVICE_BUS_SHIFT (8) + +#define MPI_FC_PORT_PGAD_PORT_MASK (0xF0000000) +#define MPI_FC_PORT_PGAD_PORT_SHIFT (28) +#define MPI_FC_PORT_PGAD_FORM_MASK (0x0F000000) +#define MPI_FC_PORT_PGAD_FORM_INDEX (0x01000000) +#define MPI_FC_PORT_PGAD_INDEX_MASK (0x0000FFFF) +#define MPI_FC_PORT_PGAD_INDEX_SHIFT (0) + +#define MPI_FC_DEVICE_PGAD_PORT_MASK (0xF0000000) +#define MPI_FC_DEVICE_PGAD_PORT_SHIFT (28) +#define MPI_FC_DEVICE_PGAD_FORM_MASK (0x0F000000) +#define MPI_FC_DEVICE_PGAD_FORM_NEXT_DID (0x00000000) +#define MPI_FC_DEVICE_PGAD_ND_PORT_MASK (0xF0000000) +#define MPI_FC_DEVICE_PGAD_ND_PORT_SHIFT (28) +#define MPI_FC_DEVICE_PGAD_ND_DID_MASK (0x00FFFFFF) +#define MPI_FC_DEVICE_PGAD_ND_DID_SHIFT (0) +#define MPI_FC_DEVICE_PGAD_FORM_BUS_TID (0x01000000) +#define MPI_FC_DEVICE_PGAD_BT_BUS_MASK (0x0000FF00) +#define MPI_FC_DEVICE_PGAD_BT_BUS_SHIFT (8) +#define MPI_FC_DEVICE_PGAD_BT_TID_MASK (0x000000FF) +#define MPI_FC_DEVICE_PGAD_BT_TID_SHIFT (0) + +#define MPI_PHYSDISK_PGAD_PHYSDISKNUM_MASK (0x000000FF) +#define MPI_PHYSDISK_PGAD_PHYSDISKNUM_SHIFT (0) + + + +/**************************************************************************** +* Config Request Message +****************************************************************************/ +typedef struct _MSG_CONFIG +{ + U8 Action; /* 00h */ + U8 Reserved; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved1[3]; /* 04h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 Reserved2[8]; /* 0Ch */ + fCONFIG_PAGE_HEADER Header; /* 14h */ + U32 PageAddress; /* 18h */ + SGE_IO_UNION PageBufferSGE; /* 1Ch */ +} MSG_CONFIG, MPI_POINTER PTR_MSG_CONFIG, + Config_t, MPI_POINTER pConfig_t; + + +/**************************************************************************** +* Action field values +****************************************************************************/ +#define MPI_CONFIG_ACTION_PAGE_HEADER (0x00) +#define MPI_CONFIG_ACTION_PAGE_READ_CURRENT (0x01) +#define MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT (0x02) +#define MPI_CONFIG_ACTION_PAGE_DEFAULT (0x03) +#define MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM (0x04) +#define MPI_CONFIG_ACTION_PAGE_READ_DEFAULT (0x05) +#define MPI_CONFIG_ACTION_PAGE_READ_NVRAM (0x06) + + +/* Config Reply Message */ +typedef struct _MSG_CONFIG_REPLY +{ + U8 Action; /* 00h */ + U8 Reserved; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved1[3]; /* 04h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 Reserved2[2]; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + fCONFIG_PAGE_HEADER Header; /* 14h */ +} MSG_CONFIG_REPLY, MPI_POINTER PTR_MSG_CONFIG_REPLY, + ConfigReply_t, MPI_POINTER pConfigReply_t; + + + +/***************************************************************************** +* +* C o n f i g u r a t i o n P a g e s +* +*****************************************************************************/ + +/**************************************************************************** +* Manufacturing Config pages +****************************************************************************/ +#define MPI_MANUFACTPAGE_DEVICEID_FC909 (0x0621) +#define MPI_MANUFACTPAGE_DEVICEID_FC919 (0x0624) +#define MPI_MANUFACTPAGE_DEVICEID_FC929 (0x0622) +#define MPI_MANUFACTPAGE_DEVICEID_FC919X (0x0628) +#define MPI_MANUFACTPAGE_DEVICEID_FC929X (0x0626) + +#define MPI_MANUFACTPAGE_DEVID_53C1030 (0x0030) +#define MPI_MANUFACTPAGE_DEVID_53C1030ZC (0x0031) +#define MPI_MANUFACTPAGE_DEVID_1030_53C1035 (0x0032) +#define MPI_MANUFACTPAGE_DEVID_1030ZC_53C1035 (0x0033) +#define MPI_MANUFACTPAGE_DEVID_53C1035 (0x0040) +#define MPI_MANUFACTPAGE_DEVID_53C1035ZC (0x0041) + +#define MPI_MANUFACTPAGE_DEVID_SA2010 (0x0804) +#define MPI_MANUFACTPAGE_DEVID_SA2010ZC (0x0805) +#define MPI_MANUFACTPAGE_DEVID_SA2020 (0x0806) +#define MPI_MANUFACTPAGE_DEVID_SA2020ZC (0x0807) + + +typedef struct _CONFIG_PAGE_MANUFACTURING_0 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U8 ChipName[16]; /* 04h */ + U8 ChipRevision[8]; /* 14h */ + U8 BoardName[16]; /* 1Ch */ + U8 BoardAssembly[16]; /* 2Ch */ + U8 BoardTracerNumber[16]; /* 3Ch */ + +} fCONFIG_PAGE_MANUFACTURING_0, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_0, + ManufacturingPage0_t, MPI_POINTER pManufacturingPage0_t; + +#define MPI_MANUFACTURING0_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_MANUFACTURING_1 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U8 VPD[256]; /* 04h */ +} fCONFIG_PAGE_MANUFACTURING_1, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_1, + ManufacturingPage1_t, MPI_POINTER pManufacturingPage1_t; + +#define MPI_MANUFACTURING1_PAGEVERSION (0x00) + + +typedef struct _MPI_CHIP_REVISION_ID +{ + U16 DeviceID; /* 00h */ + U8 PCIRevisionID; /* 02h */ + U8 Reserved; /* 03h */ +} MPI_CHIP_REVISION_ID, MPI_POINTER PTR_MPI_CHIP_REVISION_ID, + MpiChipRevisionId_t, MPI_POINTER pMpiChipRevisionId_t; + + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI_MAN_PAGE_2_HW_SETTINGS_WORDS +#define MPI_MAN_PAGE_2_HW_SETTINGS_WORDS (1) +#endif + +typedef struct _CONFIG_PAGE_MANUFACTURING_2 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + MPI_CHIP_REVISION_ID ChipId; /* 04h */ + U32 HwSettings[MPI_MAN_PAGE_2_HW_SETTINGS_WORDS];/* 08h */ +} fCONFIG_PAGE_MANUFACTURING_2, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_2, + ManufacturingPage2_t, MPI_POINTER pManufacturingPage2_t; + +#define MPI_MANUFACTURING2_PAGEVERSION (0x00) + + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI_MAN_PAGE_3_INFO_WORDS +#define MPI_MAN_PAGE_3_INFO_WORDS (1) +#endif + +typedef struct _CONFIG_PAGE_MANUFACTURING_3 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + MPI_CHIP_REVISION_ID ChipId; /* 04h */ + U32 Info[MPI_MAN_PAGE_3_INFO_WORDS];/* 08h */ +} fCONFIG_PAGE_MANUFACTURING_3, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_3, + ManufacturingPage3_t, MPI_POINTER pManufacturingPage3_t; + +#define MPI_MANUFACTURING3_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_MANUFACTURING_4 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 Reserved1; /* 04h */ + U8 InfoOffset0; /* 08h */ + U8 InfoSize0; /* 09h */ + U8 InfoOffset1; /* 0Ah */ + U8 InfoSize1; /* 0Bh */ + U8 InquirySize; /* 0Ch */ + U8 Reserved2; /* 0Dh */ + U16 Reserved3; /* 0Eh */ + U8 InquiryData[56]; /* 10h */ + U32 ISVolumeSettings; /* 48h */ + U32 IMEVolumeSettings; /* 4Ch */ + U32 IMVolumeSettings; /* 50h */ +} fCONFIG_PAGE_MANUFACTURING_4, MPI_POINTER PTR_CONFIG_PAGE_MANUFACTURING_4, + ManufacturingPage4_t, MPI_POINTER pManufacturingPage4_t; + +#define MPI_MANUFACTURING4_PAGEVERSION (0x00) + + +/**************************************************************************** +* IO Unit Config Pages +****************************************************************************/ + +typedef struct _CONFIG_PAGE_IO_UNIT_0 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U64 UniqueValue; /* 04h */ +} fCONFIG_PAGE_IO_UNIT_0, MPI_POINTER PTR_CONFIG_PAGE_IO_UNIT_0, + IOUnitPage0_t, MPI_POINTER pIOUnitPage0_t; + +#define MPI_IOUNITPAGE0_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_IO_UNIT_1 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 Flags; /* 04h */ +} fCONFIG_PAGE_IO_UNIT_1, MPI_POINTER PTR_CONFIG_PAGE_IO_UNIT_1, + IOUnitPage1_t, MPI_POINTER pIOUnitPage1_t; + +#define MPI_IOUNITPAGE1_PAGEVERSION (0x00) + +/* IO Unit Page 1 Flags defines */ + +#define MPI_IOUNITPAGE1_MULTI_FUNCTION (0x00000000) +#define MPI_IOUNITPAGE1_SINGLE_FUNCTION (0x00000001) +#define MPI_IOUNITPAGE1_MULTI_PATHING (0x00000002) +#define MPI_IOUNITPAGE1_SINGLE_PATHING (0x00000000) +#define MPI_IOUNITPAGE1_DISABLE_IR (0x00000040) +#define MPI_IOUNITPAGE1_FORCE_32 (0x00000080) + + +typedef struct _MPI_ADAPTER_INFO +{ + U8 PciBusNumber; /* 00h */ + U8 PciDeviceAndFunctionNumber; /* 01h */ + U16 AdapterFlags; /* 02h */ +} MPI_ADAPTER_INFO, MPI_POINTER PTR_MPI_ADAPTER_INFO, + MpiAdapterInfo_t, MPI_POINTER pMpiAdapterInfo_t; + +#define MPI_ADAPTER_INFO_FLAGS_EMBEDDED (0x0001) +#define MPI_ADAPTER_INFO_FLAGS_INIT_STATUS (0x0002) + +typedef struct _CONFIG_PAGE_IO_UNIT_2 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 Flags; /* 04h */ + U32 BiosVersion; /* 08h */ + MPI_ADAPTER_INFO AdapterOrder[4]; /* 0Ch */ +} fCONFIG_PAGE_IO_UNIT_2, MPI_POINTER PTR_CONFIG_PAGE_IO_UNIT_2, + IOUnitPage2_t, MPI_POINTER pIOUnitPage2_t; + +#define MPI_IOUNITPAGE2_PAGEVERSION (0x00) + +#define MPI_IOUNITPAGE2_FLAGS_PAUSE_ON_ERROR (0x00000002) +#define MPI_IOUNITPAGE2_FLAGS_VERBOSE_ENABLE (0x00000004) +#define MPI_IOUNITPAGE2_FLAGS_COLOR_VIDEO_DISABLE (0x00000008) +#define MPI_IOUNITPAGE2_FLAGS_DONT_HOOK_INT_40 (0x00000010) + + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI_IO_UNIT_PAGE_3_GPIO_VAL_MAX +#define MPI_IO_UNIT_PAGE_3_GPIO_VAL_MAX (1) +#endif + +typedef struct _CONFIG_PAGE_IO_UNIT_3 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U8 GPIOCount; /* 04h */ + U8 Reserved1; /* 05h */ + U16 Reserved2; /* 06h */ + U16 GPIOVal[MPI_IO_UNIT_PAGE_3_GPIO_VAL_MAX]; /* 08h */ +} fCONFIG_PAGE_IO_UNIT_3, MPI_POINTER PTR_CONFIG_PAGE_IO_UNIT_3, + IOUnitPage3_t, MPI_POINTER pIOUnitPage3_t; + +#define MPI_IOUNITPAGE3_PAGEVERSION (0x01) + +#define MPI_IOUNITPAGE3_GPIO_FUNCTION_MASK (0xFC) +#define MPI_IOUNITPAGE3_GPIO_FUNCTION_SHIFT (2) +#define MPI_IOUNITPAGE3_GPIO_SETTING_OFF (0x00) +#define MPI_IOUNITPAGE3_GPIO_SETTING_ON (0x01) + + +/**************************************************************************** +* IOC Config Pages +****************************************************************************/ + +typedef struct _CONFIG_PAGE_IOC_0 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 TotalNVStore; /* 04h */ + U32 FreeNVStore; /* 08h */ + U16 VendorID; /* 0Ch */ + U16 DeviceID; /* 0Eh */ + U8 RevisionID; /* 10h */ + U8 Reserved[3]; /* 11h */ + U32 ClassCode; /* 14h */ + U16 SubsystemVendorID; /* 18h */ + U16 SubsystemID; /* 1Ah */ +} fCONFIG_PAGE_IOC_0, MPI_POINTER PTR_CONFIG_PAGE_IOC_0, + IOCPage0_t, MPI_POINTER pIOCPage0_t; + +#define MPI_IOCPAGE0_PAGEVERSION (0x01) + + +typedef struct _CONFIG_PAGE_IOC_1 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 Flags; /* 04h */ + U32 CoalescingTimeout; /* 08h */ + U8 CoalescingDepth; /* 0Ch */ + U8 PCISlotNum; /* 0Dh */ + U8 Reserved[2]; /* 0Eh */ +} fCONFIG_PAGE_IOC_1, MPI_POINTER PTR_CONFIG_PAGE_IOC_1, + IOCPage1_t, MPI_POINTER pIOCPage1_t; + +#define MPI_IOCPAGE1_PAGEVERSION (0x01) + +#define MPI_IOCPAGE1_REPLY_COALESCING (0x00000001) + +#define MPI_IOCPAGE1_PCISLOTNUM_UNKNOWN (0xFF) + + +typedef struct _CONFIG_PAGE_IOC_2_RAID_VOL +{ + U8 VolumeID; /* 00h */ + U8 VolumeBus; /* 01h */ + U8 VolumeIOC; /* 02h */ + U8 VolumePageNumber; /* 03h */ + U8 VolumeType; /* 04h */ + U8 Flags; /* 05h */ + U16 Reserved3; /* 06h */ +} fCONFIG_PAGE_IOC_2_RAID_VOL, MPI_POINTER PTR_CONFIG_PAGE_IOC_2_RAID_VOL, + ConfigPageIoc2RaidVol_t, MPI_POINTER pConfigPageIoc2RaidVol_t; + +/* IOC Page 2 Volume RAID Type values, also used in RAID Volume pages */ + +#define MPI_RAID_VOL_TYPE_IS (0x00) +#define MPI_RAID_VOL_TYPE_IME (0x01) +#define MPI_RAID_VOL_TYPE_IM (0x02) + +/* IOC Page 2 Volume Flags values */ + +#define MPI_IOCPAGE2_FLAG_VOLUME_INACTIVE (0x08) + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI_IOC_PAGE_2_RAID_VOLUME_MAX +#define MPI_IOC_PAGE_2_RAID_VOLUME_MAX (1) +#endif + +typedef struct _CONFIG_PAGE_IOC_2 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 CapabilitiesFlags; /* 04h */ + U8 NumActiveVolumes; /* 08h */ + U8 MaxVolumes; /* 09h */ + U8 NumActivePhysDisks; /* 0Ah */ + U8 MaxPhysDisks; /* 0Bh */ + fCONFIG_PAGE_IOC_2_RAID_VOL RaidVolume[MPI_IOC_PAGE_2_RAID_VOLUME_MAX];/* 0Ch */ +} fCONFIG_PAGE_IOC_2, MPI_POINTER PTR_CONFIG_PAGE_IOC_2, + IOCPage2_t, MPI_POINTER pIOCPage2_t; + +#define MPI_IOCPAGE2_PAGEVERSION (0x02) + +/* IOC Page 2 Capabilities flags */ + +#define MPI_IOCPAGE2_CAP_FLAGS_IS_SUPPORT (0x00000001) +#define MPI_IOCPAGE2_CAP_FLAGS_IME_SUPPORT (0x00000002) +#define MPI_IOCPAGE2_CAP_FLAGS_IM_SUPPORT (0x00000004) +#define MPI_IOCPAGE2_CAP_FLAGS_SES_SUPPORT (0x20000000) +#define MPI_IOCPAGE2_CAP_FLAGS_SAFTE_SUPPORT (0x40000000) +#define MPI_IOCPAGE2_CAP_FLAGS_CROSS_CHANNEL_SUPPORT (0x80000000) + + +typedef struct _IOC_3_PHYS_DISK +{ + U8 PhysDiskID; /* 00h */ + U8 PhysDiskBus; /* 01h */ + U8 PhysDiskIOC; /* 02h */ + U8 PhysDiskNum; /* 03h */ +} IOC_3_PHYS_DISK, MPI_POINTER PTR_IOC_3_PHYS_DISK, + Ioc3PhysDisk_t, MPI_POINTER pIoc3PhysDisk_t; + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI_IOC_PAGE_3_PHYSDISK_MAX +#define MPI_IOC_PAGE_3_PHYSDISK_MAX (1) +#endif + +typedef struct _CONFIG_PAGE_IOC_3 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U8 NumPhysDisks; /* 04h */ + U8 Reserved1; /* 05h */ + U16 Reserved2; /* 06h */ + IOC_3_PHYS_DISK PhysDisk[MPI_IOC_PAGE_3_PHYSDISK_MAX]; /* 08h */ +} fCONFIG_PAGE_IOC_3, MPI_POINTER PTR_CONFIG_PAGE_IOC_3, + IOCPage3_t, MPI_POINTER pIOCPage3_t; + +#define MPI_IOCPAGE3_PAGEVERSION (0x00) + + +typedef struct _IOC_4_SEP +{ + U8 SEPTargetID; /* 00h */ + U8 SEPBus; /* 01h */ + U16 Reserved; /* 02h */ +} IOC_4_SEP, MPI_POINTER PTR_IOC_4_SEP, + Ioc4Sep_t, MPI_POINTER pIoc4Sep_t; + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI_IOC_PAGE_4_SEP_MAX +#define MPI_IOC_PAGE_4_SEP_MAX (1) +#endif + +typedef struct _CONFIG_PAGE_IOC_4 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U8 ActiveSEP; /* 04h */ + U8 MaxSEP; /* 05h */ + U16 Reserved1; /* 06h */ + IOC_4_SEP SEP[MPI_IOC_PAGE_4_SEP_MAX]; /* 08h */ +} fCONFIG_PAGE_IOC_4, MPI_POINTER PTR_CONFIG_PAGE_IOC_4, + IOCPage4_t, MPI_POINTER pIOCPage4_t; + +#define MPI_IOCPAGE4_PAGEVERSION (0x00) + + +typedef struct _IOC_5_HOT_SPARE +{ + U8 PhysDiskNum; /* 00h */ + U8 Reserved; /* 01h */ + U8 HotSparePool; /* 02h */ + U8 Flags; /* 03h */ +} IOC_5_HOT_SPARE, MPI_POINTER PTR_IOC_5_HOT_SPARE, + Ioc5HotSpare_t, MPI_POINTER pIoc5HotSpare_t; + +/* IOC Page 5 HotSpare Flags */ +#define MPI_IOC_PAGE_5_HOT_SPARE_ACTIVE (0x01) + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI_IOC_PAGE_5_HOT_SPARE_MAX +#define MPI_IOC_PAGE_5_HOT_SPARE_MAX (1) +#endif + +typedef struct _CONFIG_PAGE_IOC_5 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 Reserved1; /* 04h */ + U8 NumHotSpares; /* 08h */ + U8 Reserved2; /* 09h */ + U16 Reserved3; /* 0Ah */ + IOC_5_HOT_SPARE HotSpare[MPI_IOC_PAGE_5_HOT_SPARE_MAX]; /* 0Ch */ +} fCONFIG_PAGE_IOC_5, MPI_POINTER PTR_CONFIG_PAGE_IOC_5, + IOCPage5_t, MPI_POINTER pIOCPage5_t; + +#define MPI_IOCPAGE5_PAGEVERSION (0x00) + + + +/**************************************************************************** +* SCSI Port Config Pages +****************************************************************************/ + +typedef struct _CONFIG_PAGE_SCSI_PORT_0 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 Capabilities; /* 04h */ + U32 PhysicalInterface; /* 08h */ +} fCONFIG_PAGE_SCSI_PORT_0, MPI_POINTER PTR_CONFIG_PAGE_SCSI_PORT_0, + SCSIPortPage0_t, MPI_POINTER pSCSIPortPage0_t; + +#define MPI_SCSIPORTPAGE0_PAGEVERSION (0x01) + +#define MPI_SCSIPORTPAGE0_CAP_IU (0x00000001) +#define MPI_SCSIPORTPAGE0_CAP_DT (0x00000002) +#define MPI_SCSIPORTPAGE0_CAP_QAS (0x00000004) +#define MPI_SCSIPORTPAGE0_CAP_MIN_SYNC_PERIOD_MASK (0x0000FF00) +#define MPI_SCSIPORTPAGE0_CAP_MAX_SYNC_OFFSET_MASK (0x00FF0000) +#define MPI_SCSIPORTPAGE0_CAP_WIDE (0x20000000) +#define MPI_SCSIPORTPAGE0_CAP_AIP (0x80000000) + +#define MPI_SCSIPORTPAGE0_PHY_SIGNAL_TYPE_MASK (0x00000003) +#define MPI_SCSIPORTPAGE0_PHY_SIGNAL_HVD (0x01) +#define MPI_SCSIPORTPAGE0_PHY_SIGNAL_SE (0x02) +#define MPI_SCSIPORTPAGE0_PHY_SIGNAL_LVD (0x03) + + +typedef struct _CONFIG_PAGE_SCSI_PORT_1 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 Configuration; /* 04h */ + U32 OnBusTimerValue; /* 08h */ +} fCONFIG_PAGE_SCSI_PORT_1, MPI_POINTER PTR_CONFIG_PAGE_SCSI_PORT_1, + SCSIPortPage1_t, MPI_POINTER pSCSIPortPage1_t; + +#define MPI_SCSIPORTPAGE1_PAGEVERSION (0x02) + +#define MPI_SCSIPORTPAGE1_CFG_PORT_SCSI_ID_MASK (0x000000FF) +#define MPI_SCSIPORTPAGE1_CFG_PORT_RESPONSE_ID_MASK (0xFFFF0000) + + +typedef struct _MPI_DEVICE_INFO +{ + U8 Timeout; /* 00h */ + U8 SyncFactor; /* 01h */ + U16 DeviceFlags; /* 02h */ +} MPI_DEVICE_INFO, MPI_POINTER PTR_MPI_DEVICE_INFO, + MpiDeviceInfo_t, MPI_POINTER pMpiDeviceInfo_t; + +typedef struct _CONFIG_PAGE_SCSI_PORT_2 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 PortFlags; /* 04h */ + U32 PortSettings; /* 08h */ + MPI_DEVICE_INFO DeviceSettings[16]; /* 0Ch */ +} fCONFIG_PAGE_SCSI_PORT_2, MPI_POINTER PTR_CONFIG_PAGE_SCSI_PORT_2, + SCSIPortPage2_t, MPI_POINTER pSCSIPortPage2_t; + +#define MPI_SCSIPORTPAGE2_PAGEVERSION (0x01) + +#define MPI_SCSIPORTPAGE2_PORT_FLAGS_SCAN_HIGH_TO_LOW (0x00000001) +#define MPI_SCSIPORTPAGE2_PORT_FLAGS_AVOID_SCSI_RESET (0x00000004) +#define MPI_SCSIPORTPAGE2_PORT_FLAGS_ALTERNATE_CHS (0x00000008) +#define MPI_SCSIPORTPAGE2_PORT_FLAGS_TERMINATION_DISABLE (0x00000010) + +#define MPI_SCSIPORTPAGE2_PORT_HOST_ID_MASK (0x0000000F) +#define MPI_SCSIPORTPAGE2_PORT_MASK_INIT_HBA (0x00000030) +#define MPI_SCSIPORTPAGE2_PORT_DISABLE_INIT_HBA (0x00000000) +#define MPI_SCSIPORTPAGE2_PORT_BIOS_INIT_HBA (0x00000010) +#define MPI_SCSIPORTPAGE2_PORT_OS_INIT_HBA (0x00000020) +#define MPI_SCSIPORTPAGE2_PORT_BIOS_OS_INIT_HBA (0x00000030) +#define MPI_SCSIPORTPAGE2_PORT_REMOVABLE_MEDIA (0x000000C0) +#define MPI_SCSIPORTPAGE2_PORT_SPINUP_DELAY_MASK (0x00000F00) +#define MPI_SCSIPORTPAGE2_PORT_MASK_NEGO_MASTER_SETTINGS (0x00003000) +#define MPI_SCSIPORTPAGE2_PORT_NEGO_MASTER_SETTINGS (0x00000000) +#define MPI_SCSIPORTPAGE2_PORT_NONE_MASTER_SETTINGS (0x00001000) +#define MPI_SCSIPORTPAGE2_PORT_ALL_MASTER_SETTINGS (0x00003000) + +#define MPI_SCSIPORTPAGE2_DEVICE_DISCONNECT_ENABLE (0x0001) +#define MPI_SCSIPORTPAGE2_DEVICE_ID_SCAN_ENABLE (0x0002) +#define MPI_SCSIPORTPAGE2_DEVICE_LUN_SCAN_ENABLE (0x0004) +#define MPI_SCSIPORTPAGE2_DEVICE_TAG_QUEUE_ENABLE (0x0008) +#define MPI_SCSIPORTPAGE2_DEVICE_WIDE_DISABLE (0x0010) +#define MPI_SCSIPORTPAGE2_DEVICE_BOOT_CHOICE (0x0020) + + +/**************************************************************************** +* SCSI Target Device Config Pages +****************************************************************************/ + +typedef struct _CONFIG_PAGE_SCSI_DEVICE_0 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 NegotiatedParameters; /* 04h */ + U32 Information; /* 08h */ +} fCONFIG_PAGE_SCSI_DEVICE_0, MPI_POINTER PTR_CONFIG_PAGE_SCSI_DEVICE_0, + SCSIDevicePage0_t, MPI_POINTER pSCSIDevicePage0_t; + +#define MPI_SCSIDEVPAGE0_PAGEVERSION (0x03) + +#define MPI_SCSIDEVPAGE0_NP_IU (0x00000001) +#define MPI_SCSIDEVPAGE0_NP_DT (0x00000002) +#define MPI_SCSIDEVPAGE0_NP_QAS (0x00000004) +#define MPI_SCSIDEVPAGE0_NP_HOLD_MCS (0x00000008) +#define MPI_SCSIDEVPAGE0_NP_WR_FLOW (0x00000010) +#define MPI_SCSIDEVPAGE0_NP_RD_STRM (0x00000020) +#define MPI_SCSIDEVPAGE0_NP_RTI (0x00000040) +#define MPI_SCSIDEVPAGE0_NP_PCOMP_EN (0x00000080) +#define MPI_SCSIDEVPAGE0_NP_NEG_SYNC_PERIOD_MASK (0x0000FF00) +#define MPI_SCSIDEVPAGE0_NP_NEG_SYNC_OFFSET_MASK (0x00FF0000) +#define MPI_SCSIDEVPAGE0_NP_WIDE (0x20000000) +#define MPI_SCSIDEVPAGE0_NP_AIP (0x80000000) + +#define MPI_SCSIDEVPAGE0_INFO_PARAMS_NEGOTIATED (0x00000001) +#define MPI_SCSIDEVPAGE0_INFO_SDTR_REJECTED (0x00000002) +#define MPI_SCSIDEVPAGE0_INFO_WDTR_REJECTED (0x00000004) +#define MPI_SCSIDEVPAGE0_INFO_PPR_REJECTED (0x00000008) + + +typedef struct _CONFIG_PAGE_SCSI_DEVICE_1 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 RequestedParameters; /* 04h */ + U32 Reserved; /* 08h */ + U32 Configuration; /* 0Ch */ +} fCONFIG_PAGE_SCSI_DEVICE_1, MPI_POINTER PTR_CONFIG_PAGE_SCSI_DEVICE_1, + SCSIDevicePage1_t, MPI_POINTER pSCSIDevicePage1_t; + +#define MPI_SCSIDEVPAGE1_PAGEVERSION (0x04) + +#define MPI_SCSIDEVPAGE1_RP_IU (0x00000001) +#define MPI_SCSIDEVPAGE1_RP_DT (0x00000002) +#define MPI_SCSIDEVPAGE1_RP_QAS (0x00000004) +#define MPI_SCSIDEVPAGE1_RP_HOLD_MCS (0x00000008) +#define MPI_SCSIDEVPAGE1_RP_WR_FLOW (0x00000010) +#define MPI_SCSIDEVPAGE1_RP_RD_STRM (0x00000020) +#define MPI_SCSIDEVPAGE1_RP_RTI (0x00000040) +#define MPI_SCSIDEVPAGE1_RP_PCOMP_EN (0x00000080) +#define MPI_SCSIDEVPAGE1_RP_MIN_SYNC_PERIOD_MASK (0x0000FF00) +#define MPI_SCSIDEVPAGE1_RP_MAX_SYNC_OFFSET_MASK (0x00FF0000) +#define MPI_SCSIDEVPAGE1_RP_WIDE (0x20000000) +#define MPI_SCSIDEVPAGE1_RP_AIP (0x80000000) + +#define MPI_SCSIDEVPAGE1_CONF_WDTR_DISALLOWED (0x00000002) +#define MPI_SCSIDEVPAGE1_CONF_SDTR_DISALLOWED (0x00000004) +#define MPI_SCSIDEVPAGE1_CONF_EXTENDED_PARAMS_ENABLE (0x00000008) +#define MPI_SCSIDEVPAGE1_CONF_FORCE_PPR_MSG (0x00000010) + + +typedef struct _CONFIG_PAGE_SCSI_DEVICE_2 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 DomainValidation; /* 04h */ + U32 ParityPipeSelect; /* 08h */ + U32 DataPipeSelect; /* 0Ch */ +} fCONFIG_PAGE_SCSI_DEVICE_2, MPI_POINTER PTR_CONFIG_PAGE_SCSI_DEVICE_2, + SCSIDevicePage2_t, MPI_POINTER pSCSIDevicePage2_t; + +#define MPI_SCSIDEVPAGE2_PAGEVERSION (0x01) + +#define MPI_SCSIDEVPAGE2_DV_ISI_ENABLE (0x00000010) +#define MPI_SCSIDEVPAGE2_DV_SECONDARY_DRIVER_ENABLE (0x00000020) +#define MPI_SCSIDEVPAGE2_DV_SLEW_RATE_CTRL (0x00000380) +#define MPI_SCSIDEVPAGE2_DV_PRIM_DRIVE_STR_CTRL (0x00001C00) +#define MPI_SCSIDEVPAGE2_DV_SECOND_DRIVE_STR_CTRL (0x0000E000) +#define MPI_SCSIDEVPAGE2_DV_XCLKH_ST (0x10000000) +#define MPI_SCSIDEVPAGE2_DV_XCLKS_ST (0x20000000) +#define MPI_SCSIDEVPAGE2_DV_XCLKH_DT (0x40000000) +#define MPI_SCSIDEVPAGE2_DV_XCLKS_DT (0x80000000) + +#define MPI_SCSIDEVPAGE2_PPS_PPS_MASK (0x00000003) + +#define MPI_SCSIDEVPAGE2_DPS_BIT_0_PL_SELECT_MASK (0x00000003) +#define MPI_SCSIDEVPAGE2_DPS_BIT_1_PL_SELECT_MASK (0x0000000C) +#define MPI_SCSIDEVPAGE2_DPS_BIT_2_PL_SELECT_MASK (0x00000030) +#define MPI_SCSIDEVPAGE2_DPS_BIT_3_PL_SELECT_MASK (0x000000C0) +#define MPI_SCSIDEVPAGE2_DPS_BIT_4_PL_SELECT_MASK (0x00000300) +#define MPI_SCSIDEVPAGE2_DPS_BIT_5_PL_SELECT_MASK (0x00000C00) +#define MPI_SCSIDEVPAGE2_DPS_BIT_6_PL_SELECT_MASK (0x00003000) +#define MPI_SCSIDEVPAGE2_DPS_BIT_7_PL_SELECT_MASK (0x0000C000) +#define MPI_SCSIDEVPAGE2_DPS_BIT_8_PL_SELECT_MASK (0x00030000) +#define MPI_SCSIDEVPAGE2_DPS_BIT_9_PL_SELECT_MASK (0x000C0000) +#define MPI_SCSIDEVPAGE2_DPS_BIT_10_PL_SELECT_MASK (0x00300000) +#define MPI_SCSIDEVPAGE2_DPS_BIT_11_PL_SELECT_MASK (0x00C00000) +#define MPI_SCSIDEVPAGE2_DPS_BIT_12_PL_SELECT_MASK (0x03000000) +#define MPI_SCSIDEVPAGE2_DPS_BIT_13_PL_SELECT_MASK (0x0C000000) +#define MPI_SCSIDEVPAGE2_DPS_BIT_14_PL_SELECT_MASK (0x30000000) +#define MPI_SCSIDEVPAGE2_DPS_BIT_15_PL_SELECT_MASK (0xC0000000) + + +typedef struct _CONFIG_PAGE_SCSI_DEVICE_3 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U16 MsgRejectCount; /* 04h */ + U16 PhaseErrorCount; /* 06h */ + U16 ParityErrorCount; /* 08h */ + U16 Reserved; /* 0Ah */ +} fCONFIG_PAGE_SCSI_DEVICE_3, MPI_POINTER PTR_CONFIG_PAGE_SCSI_DEVICE_3, + SCSIDevicePage3_t, MPI_POINTER pSCSIDevicePage3_t; + +#define MPI_SCSIDEVPAGE3_PAGEVERSION (0x00) + +#define MPI_SCSIDEVPAGE3_MAX_COUNTER (0xFFFE) +#define MPI_SCSIDEVPAGE3_UNSUPPORTED_COUNTER (0xFFFF) + + +/**************************************************************************** +* FC Port Config Pages +****************************************************************************/ + +typedef struct _CONFIG_PAGE_FC_PORT_0 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 Flags; /* 04h */ + U8 MPIPortNumber; /* 08h */ + U8 LinkType; /* 09h */ + U8 PortState; /* 0Ah */ + U8 Reserved; /* 0Bh */ + U32 PortIdentifier; /* 0Ch */ + U64 WWNN; /* 10h */ + U64 WWPN; /* 18h */ + U32 SupportedServiceClass; /* 20h */ + U32 SupportedSpeeds; /* 24h */ + U32 CurrentSpeed; /* 28h */ + U32 MaxFrameSize; /* 2Ch */ + U64 FabricWWNN; /* 30h */ + U64 FabricWWPN; /* 38h */ + U32 DiscoveredPortsCount; /* 40h */ + U32 MaxInitiators; /* 44h */ + U8 MaxAliasesSupported; /* 48h */ + U8 MaxHardAliasesSupported; /* 49h */ + U8 NumCurrentAliases; /* 4Ah */ + U8 Reserved1; /* 4Bh */ +} fCONFIG_PAGE_FC_PORT_0, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_0, + FCPortPage0_t, MPI_POINTER pFCPortPage0_t; + +#define MPI_FCPORTPAGE0_PAGEVERSION (0x02) + +#define MPI_FCPORTPAGE0_FLAGS_PROT_MASK (0x0000000F) +#define MPI_FCPORTPAGE0_FLAGS_PROT_FCP_INIT (MPI_PORTFACTS_PROTOCOL_INITIATOR) +#define MPI_FCPORTPAGE0_FLAGS_PROT_FCP_TARG (MPI_PORTFACTS_PROTOCOL_TARGET) +#define MPI_FCPORTPAGE0_FLAGS_PROT_LAN (MPI_PORTFACTS_PROTOCOL_LAN) +#define MPI_FCPORTPAGE0_FLAGS_PROT_LOGBUSADDR (MPI_PORTFACTS_PROTOCOL_LOGBUSADDR) + +#define MPI_FCPORTPAGE0_FLAGS_ALIAS_ALPA_SUPPORTED (0x00000010) +#define MPI_FCPORTPAGE0_FLAGS_ALIAS_WWN_SUPPORTED (0x00000020) +#define MPI_FCPORTPAGE0_FLAGS_FABRIC_WWN_VALID (0x00000030) + +#define MPI_FCPORTPAGE0_FLAGS_ATTACH_TYPE_MASK (0x00000F00) +#define MPI_FCPORTPAGE0_FLAGS_ATTACH_NO_INIT (0x00000000) +#define MPI_FCPORTPAGE0_FLAGS_ATTACH_POINT_TO_POINT (0x00000100) +#define MPI_FCPORTPAGE0_FLAGS_ATTACH_PRIVATE_LOOP (0x00000200) +#define MPI_FCPORTPAGE0_FLAGS_ATTACH_FABRIC_DIRECT (0x00000400) +#define MPI_FCPORTPAGE0_FLAGS_ATTACH_PUBLIC_LOOP (0x00000800) + +#define MPI_FCPORTPAGE0_LTYPE_RESERVED (0x00) +#define MPI_FCPORTPAGE0_LTYPE_OTHER (0x01) +#define MPI_FCPORTPAGE0_LTYPE_UNKNOWN (0x02) +#define MPI_FCPORTPAGE0_LTYPE_COPPER (0x03) +#define MPI_FCPORTPAGE0_LTYPE_SINGLE_1300 (0x04) +#define MPI_FCPORTPAGE0_LTYPE_SINGLE_1500 (0x05) +#define MPI_FCPORTPAGE0_LTYPE_50_LASER_MULTI (0x06) +#define MPI_FCPORTPAGE0_LTYPE_50_LED_MULTI (0x07) +#define MPI_FCPORTPAGE0_LTYPE_62_LASER_MULTI (0x08) +#define MPI_FCPORTPAGE0_LTYPE_62_LED_MULTI (0x09) +#define MPI_FCPORTPAGE0_LTYPE_MULTI_LONG_WAVE (0x0A) +#define MPI_FCPORTPAGE0_LTYPE_MULTI_SHORT_WAVE (0x0B) +#define MPI_FCPORTPAGE0_LTYPE_LASER_SHORT_WAVE (0x0C) +#define MPI_FCPORTPAGE0_LTYPE_LED_SHORT_WAVE (0x0D) +#define MPI_FCPORTPAGE0_LTYPE_1300_LONG_WAVE (0x0E) +#define MPI_FCPORTPAGE0_LTYPE_1500_LONG_WAVE (0x0F) + +#define MPI_FCPORTPAGE0_PORTSTATE_UNKNOWN (0x01) /*(SNIA)HBA_PORTSTATE_UNKNOWN 1 Unknown */ +#define MPI_FCPORTPAGE0_PORTSTATE_ONLINE (0x02) /*(SNIA)HBA_PORTSTATE_ONLINE 2 Operational */ +#define MPI_FCPORTPAGE0_PORTSTATE_OFFLINE (0x03) /*(SNIA)HBA_PORTSTATE_OFFLINE 3 User Offline */ +#define MPI_FCPORTPAGE0_PORTSTATE_BYPASSED (0x04) /*(SNIA)HBA_PORTSTATE_BYPASSED 4 Bypassed */ +#define MPI_FCPORTPAGE0_PORTSTATE_DIAGNOST (0x05) /*(SNIA)HBA_PORTSTATE_DIAGNOSTICS 5 In diagnostics mode */ +#define MPI_FCPORTPAGE0_PORTSTATE_LINKDOWN (0x06) /*(SNIA)HBA_PORTSTATE_LINKDOWN 6 Link Down */ +#define MPI_FCPORTPAGE0_PORTSTATE_ERROR (0x07) /*(SNIA)HBA_PORTSTATE_ERROR 7 Port Error */ +#define MPI_FCPORTPAGE0_PORTSTATE_LOOPBACK (0x08) /*(SNIA)HBA_PORTSTATE_LOOPBACK 8 Loopback */ + +#define MPI_FCPORTPAGE0_SUPPORT_CLASS_1 (0x00000001) +#define MPI_FCPORTPAGE0_SUPPORT_CLASS_2 (0x00000002) +#define MPI_FCPORTPAGE0_SUPPORT_CLASS_3 (0x00000004) + +#define MPI_FCPORTPAGE0_SUPPORT_1GBIT_SPEED (0x00000001) /* (SNIA)HBA_PORTSPEED_1GBIT 1 1 GBit/sec */ +#define MPI_FCPORTPAGE0_SUPPORT_2GBIT_SPEED (0x00000002) /* (SNIA)HBA_PORTSPEED_2GBIT 2 2 GBit/sec */ +#define MPI_FCPORTPAGE0_SUPPORT_10GBIT_SPEED (0x00000004) /* (SNIA)HBA_PORTSPEED_10GBIT 4 10 GBit/sec */ + +#define MPI_FCPORTPAGE0_CURRENT_SPEED_1GBIT MPI_FCPORTPAGE0_SUPPORT_1GBIT_SPEED +#define MPI_FCPORTPAGE0_CURRENT_SPEED_2GBIT MPI_FCPORTPAGE0_SUPPORT_2GBIT_SPEED +#define MPI_FCPORTPAGE0_CURRENT_SPEED_10GBIT MPI_FCPORTPAGE0_SUPPORT_10GBIT_SPEED + + +typedef struct _CONFIG_PAGE_FC_PORT_1 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 Flags; /* 04h */ + U64 NoSEEPROMWWNN; /* 08h */ + U64 NoSEEPROMWWPN; /* 10h */ + U8 HardALPA; /* 18h */ + U8 LinkConfig; /* 19h */ + U8 TopologyConfig; /* 1Ah */ + U8 AltConnector; /* 1Bh */ + U8 NumRequestedAliases; /* 1Ch */ + U8 Reserved1; /* 1Dh */ + U16 Reserved2; /* 1Eh */ +} fCONFIG_PAGE_FC_PORT_1, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_1, + FCPortPage1_t, MPI_POINTER pFCPortPage1_t; + +#define MPI_FCPORTPAGE1_PAGEVERSION (0x04) + +#define MPI_FCPORTPAGE1_FLAGS_EXT_FCP_STATUS_EN (0x08000000) +#define MPI_FCPORTPAGE1_FLAGS_IMMEDIATE_ERROR_REPLY (0x04000000) +#define MPI_FCPORTPAGE1_FLAGS_SORT_BY_DID (0x00000001) +#define MPI_FCPORTPAGE1_FLAGS_SORT_BY_WWN (0x00000000) + +#define MPI_FCPORTPAGE1_FLAGS_PROT_MASK (0xF0000000) +#define MPI_FCPORTPAGE1_FLAGS_PROT_SHIFT (28) +#define MPI_FCPORTPAGE1_FLAGS_PROT_FCP_INIT ((U32)MPI_PORTFACTS_PROTOCOL_INITIATOR << MPI_FCPORTPAGE1_FLAGS_PROT_SHIFT) +#define MPI_FCPORTPAGE1_FLAGS_PROT_FCP_TARG ((U32)MPI_PORTFACTS_PROTOCOL_TARGET << MPI_FCPORTPAGE1_FLAGS_PROT_SHIFT) +#define MPI_FCPORTPAGE1_FLAGS_PROT_LAN ((U32)MPI_PORTFACTS_PROTOCOL_LAN << MPI_FCPORTPAGE1_FLAGS_PROT_SHIFT) +#define MPI_FCPORTPAGE1_FLAGS_PROT_LOGBUSADDR ((U32)MPI_PORTFACTS_PROTOCOL_LOGBUSADDR << MPI_FCPORTPAGE1_FLAGS_PROT_SHIFT) + +#define MPI_FCPORTPAGE1_HARD_ALPA_NOT_USED (0xFF) + +#define MPI_FCPORTPAGE1_LCONFIG_SPEED_MASK (0x0F) +#define MPI_FCPORTPAGE1_LCONFIG_SPEED_1GIG (0x00) +#define MPI_FCPORTPAGE1_LCONFIG_SPEED_2GIG (0x01) +#define MPI_FCPORTPAGE1_LCONFIG_SPEED_4GIG (0x02) +#define MPI_FCPORTPAGE1_LCONFIG_SPEED_10GIG (0x03) +#define MPI_FCPORTPAGE1_LCONFIG_SPEED_AUTO (0x0F) + +#define MPI_FCPORTPAGE1_TOPOLOGY_MASK (0x0F) +#define MPI_FCPORTPAGE1_TOPOLOGY_NLPORT (0x01) +#define MPI_FCPORTPAGE1_TOPOLOGY_NPORT (0x02) +#define MPI_FCPORTPAGE1_TOPOLOGY_AUTO (0x0F) + +#define MPI_FCPORTPAGE1_ALT_CONN_UNKNOWN (0x00) + + +typedef struct _CONFIG_PAGE_FC_PORT_2 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U8 NumberActive; /* 04h */ + U8 ALPA[127]; /* 05h */ +} fCONFIG_PAGE_FC_PORT_2, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_2, + FCPortPage2_t, MPI_POINTER pFCPortPage2_t; + +#define MPI_FCPORTPAGE2_PAGEVERSION (0x01) + + +typedef struct _WWN_FORMAT +{ + U64 WWNN; /* 00h */ + U64 WWPN; /* 08h */ +} WWN_FORMAT, MPI_POINTER PTR_WWN_FORMAT, + WWNFormat, MPI_POINTER pWWNFormat; + +typedef union _FC_PORT_PERSISTENT_PHYSICAL_ID +{ + WWN_FORMAT WWN; + U32 Did; +} FC_PORT_PERSISTENT_PHYSICAL_ID, MPI_POINTER PTR_FC_PORT_PERSISTENT_PHYSICAL_ID, + PersistentPhysicalId_t, MPI_POINTER pPersistentPhysicalId_t; + +typedef struct _FC_PORT_PERSISTENT +{ + FC_PORT_PERSISTENT_PHYSICAL_ID PhysicalIdentifier; /* 00h */ + U8 TargetID; /* 10h */ + U8 Bus; /* 11h */ + U16 Flags; /* 12h */ +} FC_PORT_PERSISTENT, MPI_POINTER PTR_FC_PORT_PERSISTENT, + PersistentData_t, MPI_POINTER pPersistentData_t; + +#define MPI_PERSISTENT_FLAGS_SHIFT (16) +#define MPI_PERSISTENT_FLAGS_ENTRY_VALID (0x0001) +#define MPI_PERSISTENT_FLAGS_SCAN_ID (0x0002) +#define MPI_PERSISTENT_FLAGS_SCAN_LUNS (0x0004) +#define MPI_PERSISTENT_FLAGS_BOOT_DEVICE (0x0008) +#define MPI_PERSISTENT_FLAGS_BY_DID (0x0080) + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI_FC_PORT_PAGE_3_ENTRY_MAX +#define MPI_FC_PORT_PAGE_3_ENTRY_MAX (1) +#endif + +typedef struct _CONFIG_PAGE_FC_PORT_3 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + FC_PORT_PERSISTENT Entry[MPI_FC_PORT_PAGE_3_ENTRY_MAX]; /* 04h */ +} fCONFIG_PAGE_FC_PORT_3, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_3, + FCPortPage3_t, MPI_POINTER pFCPortPage3_t; + +#define MPI_FCPORTPAGE3_PAGEVERSION (0x01) + + +typedef struct _CONFIG_PAGE_FC_PORT_4 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 PortFlags; /* 04h */ + U32 PortSettings; /* 08h */ +} fCONFIG_PAGE_FC_PORT_4, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_4, + FCPortPage4_t, MPI_POINTER pFCPortPage4_t; + +#define MPI_FCPORTPAGE4_PAGEVERSION (0x00) + +#define MPI_FCPORTPAGE4_PORT_FLAGS_ALTERNATE_CHS (0x00000008) + +#define MPI_FCPORTPAGE4_PORT_MASK_INIT_HBA (0x00000030) +#define MPI_FCPORTPAGE4_PORT_DISABLE_INIT_HBA (0x00000000) +#define MPI_FCPORTPAGE4_PORT_BIOS_INIT_HBA (0x00000010) +#define MPI_FCPORTPAGE4_PORT_OS_INIT_HBA (0x00000020) +#define MPI_FCPORTPAGE4_PORT_BIOS_OS_INIT_HBA (0x00000030) +#define MPI_FCPORTPAGE4_PORT_REMOVABLE_MEDIA (0x000000C0) +#define MPI_FCPORTPAGE4_PORT_SPINUP_DELAY_MASK (0x00000F00) + + +typedef struct _CONFIG_PAGE_FC_PORT_5_ALIAS_INFO +{ + U8 Flags; /* 00h */ + U8 AliasAlpa; /* 01h */ + U16 Reserved; /* 02h */ + U64 AliasWWNN; /* 04h */ + U64 AliasWWPN; /* 0Ch */ +} fCONFIG_PAGE_FC_PORT_5_ALIAS_INFO, + MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_5_ALIAS_INFO, + FcPortPage5AliasInfo_t, MPI_POINTER pFcPortPage5AliasInfo_t; + +typedef struct _CONFIG_PAGE_FC_PORT_5 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + fCONFIG_PAGE_FC_PORT_5_ALIAS_INFO AliasInfo; /* 04h */ +} fCONFIG_PAGE_FC_PORT_5, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_5, + FCPortPage5_t, MPI_POINTER pFCPortPage5_t; + +#define MPI_FCPORTPAGE5_PAGEVERSION (0x01) + +#define MPI_FCPORTPAGE5_FLAGS_ALPA_ACQUIRED (0x01) +#define MPI_FCPORTPAGE5_FLAGS_HARD_ALPA (0x02) +#define MPI_FCPORTPAGE5_FLAGS_HARD_WWNN (0x04) +#define MPI_FCPORTPAGE5_FLAGS_HARD_WWPN (0x08) + +typedef struct _CONFIG_PAGE_FC_PORT_6 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 Reserved; /* 04h */ + U64 TimeSinceReset; /* 08h */ + U64 TxFrames; /* 10h */ + U64 RxFrames; /* 18h */ + U64 TxWords; /* 20h */ + U64 RxWords; /* 28h */ + U64 LipCount; /* 30h */ + U64 NosCount; /* 38h */ + U64 ErrorFrames; /* 40h */ + U64 DumpedFrames; /* 48h */ + U64 LinkFailureCount; /* 50h */ + U64 LossOfSyncCount; /* 58h */ + U64 LossOfSignalCount; /* 60h */ + U64 PrimativeSeqErrCount; /* 68h */ + U64 InvalidTxWordCount; /* 70h */ + U64 InvalidCrcCount; /* 78h */ + U64 FcpInitiatorIoCount; /* 80h */ +} fCONFIG_PAGE_FC_PORT_6, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_6, + FCPortPage6_t, MPI_POINTER pFCPortPage6_t; + +#define MPI_FCPORTPAGE6_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_FC_PORT_7 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 Reserved; /* 04h */ + U8 PortSymbolicName[256]; /* 08h */ +} fCONFIG_PAGE_FC_PORT_7, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_7, + FCPortPage7_t, MPI_POINTER pFCPortPage7_t; + +#define MPI_FCPORTPAGE7_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_FC_PORT_8 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 BitVector[8]; /* 04h */ +} fCONFIG_PAGE_FC_PORT_8, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_8, + FCPortPage8_t, MPI_POINTER pFCPortPage8_t; + +#define MPI_FCPORTPAGE8_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_FC_PORT_9 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U32 Reserved; /* 04h */ + U64 GlobalWWPN; /* 08h */ + U64 GlobalWWNN; /* 10h */ + U32 UnitType; /* 18h */ + U32 PhysicalPortNumber; /* 1Ch */ + U32 NumAttachedNodes; /* 20h */ + U16 IPVersion; /* 24h */ + U16 UDPPortNumber; /* 26h */ + U8 IPAddress[16]; /* 28h */ + U16 Reserved1; /* 38h */ + U16 TopologyDiscoveryFlags; /* 3Ah */ +} fCONFIG_PAGE_FC_PORT_9, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_9, + FCPortPage9_t, MPI_POINTER pFCPortPage9_t; + +#define MPI_FCPORTPAGE9_PAGEVERSION (0x00) + + +typedef struct _CONFIG_PAGE_FC_PORT_10_BASE_SFP_DATA +{ + U8 Id; /* 10h */ + U8 ExtId; /* 11h */ + U8 Connector; /* 12h */ + U8 Transceiver[8]; /* 13h */ + U8 Encoding; /* 1Bh */ + U8 BitRate_100mbs; /* 1Ch */ + U8 Reserved1; /* 1Dh */ + U8 Length9u_km; /* 1Eh */ + U8 Length9u_100m; /* 1Fh */ + U8 Length50u_10m; /* 20h */ + U8 Length62p5u_10m; /* 21h */ + U8 LengthCopper_m; /* 22h */ + U8 Reseverved2; /* 22h */ + U8 VendorName[16]; /* 24h */ + U8 Reserved3; /* 34h */ + U8 VendorOUI[3]; /* 35h */ + U8 VendorPN[16]; /* 38h */ + U8 VendorRev[4]; /* 48h */ + U16 Reserved4; /* 4Ch */ + U8 Reserved5; /* 4Eh */ + U8 CC_BASE; /* 4Fh */ +} fCONFIG_PAGE_FC_PORT_10_BASE_SFP_DATA, + MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_10_BASE_SFP_DATA, + FCPortPage10BaseSfpData_t, MPI_POINTER pFCPortPage10BaseSfpData_t; + +#define MPI_FCPORT10_BASE_ID_UNKNOWN (0x00) +#define MPI_FCPORT10_BASE_ID_GBIC (0x01) +#define MPI_FCPORT10_BASE_ID_FIXED (0x02) +#define MPI_FCPORT10_BASE_ID_SFP (0x03) +#define MPI_FCPORT10_BASE_ID_SFP_MIN (0x04) +#define MPI_FCPORT10_BASE_ID_SFP_MAX (0x7F) +#define MPI_FCPORT10_BASE_ID_VEND_SPEC_MASK (0x80) + +#define MPI_FCPORT10_BASE_EXTID_UNKNOWN (0x00) +#define MPI_FCPORT10_BASE_EXTID_MODDEF1 (0x01) +#define MPI_FCPORT10_BASE_EXTID_MODDEF2 (0x02) +#define MPI_FCPORT10_BASE_EXTID_MODDEF3 (0x03) +#define MPI_FCPORT10_BASE_EXTID_SEEPROM (0x04) +#define MPI_FCPORT10_BASE_EXTID_MODDEF5 (0x05) +#define MPI_FCPORT10_BASE_EXTID_MODDEF6 (0x06) +#define MPI_FCPORT10_BASE_EXTID_MODDEF7 (0x07) +#define MPI_FCPORT10_BASE_EXTID_VNDSPC_MASK (0x80) + +#define MPI_FCPORT10_BASE_CONN_UNKNOWN (0x00) +#define MPI_FCPORT10_BASE_CONN_SC (0x01) +#define MPI_FCPORT10_BASE_CONN_COPPER1 (0x02) +#define MPI_FCPORT10_BASE_CONN_COPPER2 (0x03) +#define MPI_FCPORT10_BASE_CONN_BNC_TNC (0x04) +#define MPI_FCPORT10_BASE_CONN_COAXIAL (0x05) +#define MPI_FCPORT10_BASE_CONN_FIBERJACK (0x06) +#define MPI_FCPORT10_BASE_CONN_LC (0x07) +#define MPI_FCPORT10_BASE_CONN_MT_RJ (0x08) +#define MPI_FCPORT10_BASE_CONN_MU (0x09) +#define MPI_FCPORT10_BASE_CONN_SG (0x0A) +#define MPI_FCPORT10_BASE_CONN_OPT_PIGT (0x0B) +#define MPI_FCPORT10_BASE_CONN_RSV1_MIN (0x0C) +#define MPI_FCPORT10_BASE_CONN_RSV1_MAX (0x1F) +#define MPI_FCPORT10_BASE_CONN_HSSDC_II (0x20) +#define MPI_FCPORT10_BASE_CONN_CPR_PIGT (0x21) +#define MPI_FCPORT10_BASE_CONN_RSV2_MIN (0x22) +#define MPI_FCPORT10_BASE_CONN_RSV2_MAX (0x7F) +#define MPI_FCPORT10_BASE_CONN_VNDSPC_MASK (0x80) + +#define MPI_FCPORT10_BASE_ENCODE_UNSPEC (0x00) +#define MPI_FCPORT10_BASE_ENCODE_8B10B (0x01) +#define MPI_FCPORT10_BASE_ENCODE_4B5B (0x02) +#define MPI_FCPORT10_BASE_ENCODE_NRZ (0x03) +#define MPI_FCPORT10_BASE_ENCODE_MANCHESTER (0x04) + + +typedef struct _CONFIG_PAGE_FC_PORT_10_EXTENDED_SFP_DATA +{ + U8 Options[2]; /* 50h */ + U8 BitRateMax; /* 52h */ + U8 BitRateMin; /* 53h */ + U8 VendorSN[16]; /* 54h */ + U8 DateCode[8]; /* 64h */ + U8 Reserved5[3]; /* 6Ch */ + U8 CC_EXT; /* 6Fh */ +} fCONFIG_PAGE_FC_PORT_10_EXTENDED_SFP_DATA, + MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_10_EXTENDED_SFP_DATA, + FCPortPage10ExtendedSfpData_t, MPI_POINTER pFCPortPage10ExtendedSfpData_t; + +#define MPI_FCPORT10_EXT_OPTION1_RATESEL (0x20) +#define MPI_FCPORT10_EXT_OPTION1_TX_DISABLE (0x10) +#define MPI_FCPORT10_EXT_OPTION1_TX_FAULT (0x08) +#define MPI_FCPORT10_EXT_OPTION1_LOS_INVERT (0x04) +#define MPI_FCPORT10_EXT_OPTION1_LOS (0x02) + + +typedef struct _CONFIG_PAGE_FC_PORT_10 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U8 Flags; /* 04h */ + U8 Reserved1; /* 05h */ + U16 Reserved2; /* 06h */ + U32 HwConfig1; /* 08h */ + U32 HwConfig2; /* 0Ch */ + fCONFIG_PAGE_FC_PORT_10_BASE_SFP_DATA Base; /* 10h */ + fCONFIG_PAGE_FC_PORT_10_EXTENDED_SFP_DATA Extended; /* 50h */ + U8 VendorSpecific[32]; /* 70h */ +} fCONFIG_PAGE_FC_PORT_10, MPI_POINTER PTR_CONFIG_PAGE_FC_PORT_10, + FCPortPage10_t, MPI_POINTER pFCPortPage10_t; + +#define MPI_FCPORTPAGE10_PAGEVERSION (0x00) + +/* standard MODDEF pin definitions (from GBIC spec.) */ +#define MPI_FCPORTPAGE10_FLAGS_MODDEF_MASK (0x00000007) +#define MPI_FCPORTPAGE10_FLAGS_MODDEF2 (0x00000001) +#define MPI_FCPORTPAGE10_FLAGS_MODDEF1 (0x00000002) +#define MPI_FCPORTPAGE10_FLAGS_MODDEF0 (0x00000004) +#define MPI_FCPORTPAGE10_FLAGS_MODDEF_NOGBIC (0x00000007) +#define MPI_FCPORTPAGE10_FLAGS_MODDEF_CPR_IEEE_CX (0x00000006) +#define MPI_FCPORTPAGE10_FLAGS_MODDEF_COPPER (0x00000005) +#define MPI_FCPORTPAGE10_FLAGS_MODDEF_OPTICAL_LW (0x00000004) +#define MPI_FCPORTPAGE10_FLAGS_MODDEF_SEEPROM (0x00000003) +#define MPI_FCPORTPAGE10_FLAGS_MODDEF_SW_OPTICAL (0x00000002) +#define MPI_FCPORTPAGE10_FLAGS_MODDEF_LX_IEEE_OPT_LW (0x00000001) +#define MPI_FCPORTPAGE10_FLAGS_MODDEF_SX_IEEE_OPT_SW (0x00000000) + +#define MPI_FCPORTPAGE10_FLAGS_CC_BASE_OK (0x00000010) +#define MPI_FCPORTPAGE10_FLAGS_CC_EXT_OK (0x00000020) + + +/**************************************************************************** +* FC Device Config Pages +****************************************************************************/ + +typedef struct _CONFIG_PAGE_FC_DEVICE_0 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U64 WWNN; /* 04h */ + U64 WWPN; /* 0Ch */ + U32 PortIdentifier; /* 14h */ + U8 Protocol; /* 18h */ + U8 Flags; /* 19h */ + U16 BBCredit; /* 1Ah */ + U16 MaxRxFrameSize; /* 1Ch */ + U8 Reserved1; /* 1Eh */ + U8 PortNumber; /* 1Fh */ + U8 FcPhLowestVersion; /* 20h */ + U8 FcPhHighestVersion; /* 21h */ + U8 CurrentTargetID; /* 22h */ + U8 CurrentBus; /* 23h */ +} fCONFIG_PAGE_FC_DEVICE_0, MPI_POINTER PTR_CONFIG_PAGE_FC_DEVICE_0, + FCDevicePage0_t, MPI_POINTER pFCDevicePage0_t; + +#define MPI_FC_DEVICE_PAGE0_PAGEVERSION (0x02) + +#define MPI_FC_DEVICE_PAGE0_FLAGS_TARGETID_BUS_VALID (0x01) + +#define MPI_FC_DEVICE_PAGE0_PROT_IP (0x01) +#define MPI_FC_DEVICE_PAGE0_PROT_FCP_TARGET (0x02) +#define MPI_FC_DEVICE_PAGE0_PROT_FCP_INITIATOR (0x04) + +#define MPI_FC_DEVICE_PAGE0_PGAD_PORT_MASK (MPI_FC_DEVICE_PGAD_PORT_MASK) +#define MPI_FC_DEVICE_PAGE0_PGAD_FORM_MASK (MPI_FC_DEVICE_PGAD_FORM_MASK) +#define MPI_FC_DEVICE_PAGE0_PGAD_FORM_NEXT_DID (MPI_FC_DEVICE_PGAD_FORM_NEXT_DID) +#define MPI_FC_DEVICE_PAGE0_PGAD_FORM_BUS_TID (MPI_FC_DEVICE_PGAD_FORM_BUS_TID) +#define MPI_FC_DEVICE_PAGE0_PGAD_DID_MASK (MPI_FC_DEVICE_PGAD_ND_DID_MASK) +#define MPI_FC_DEVICE_PAGE0_PGAD_BUS_MASK (MPI_FC_DEVICE_PGAD_BT_BUS_MASK) +#define MPI_FC_DEVICE_PAGE0_PGAD_BUS_SHIFT (MPI_FC_DEVICE_PGAD_BT_BUS_SHIFT) +#define MPI_FC_DEVICE_PAGE0_PGAD_TID_MASK (MPI_FC_DEVICE_PGAD_BT_TID_MASK) + + +/**************************************************************************** +* RAID Volume Config Pages +****************************************************************************/ + +typedef struct _RAID_VOL0_PHYS_DISK +{ + U16 Reserved; /* 00h */ + U8 PhysDiskMap; /* 02h */ + U8 PhysDiskNum; /* 03h */ +} RAID_VOL0_PHYS_DISK, MPI_POINTER PTR_RAID_VOL0_PHYS_DISK, + RaidVol0PhysDisk_t, MPI_POINTER pRaidVol0PhysDisk_t; + +#define MPI_RAIDVOL0_PHYSDISK_PRIMARY (0x01) +#define MPI_RAIDVOL0_PHYSDISK_SECONDARY (0x02) + +typedef struct _RAID_VOL0_STATUS +{ + U8 Flags; /* 00h */ + U8 State; /* 01h */ + U16 Reserved; /* 02h */ +} RAID_VOL0_STATUS, MPI_POINTER PTR_RAID_VOL0_STATUS, + RaidVol0Status_t, MPI_POINTER pRaidVol0Status_t; + +/* RAID Volume Page 0 VolumeStatus defines */ + +#define MPI_RAIDVOL0_STATUS_FLAG_ENABLED (0x01) +#define MPI_RAIDVOL0_STATUS_FLAG_QUIESCED (0x02) +#define MPI_RAIDVOL0_STATUS_FLAG_RESYNC_IN_PROGRESS (0x04) +#define MPI_RAIDVOL0_STATUS_FLAG_VOLUME_INACTIVE (0x08) + +#define MPI_RAIDVOL0_STATUS_STATE_OPTIMAL (0x00) +#define MPI_RAIDVOL0_STATUS_STATE_DEGRADED (0x01) +#define MPI_RAIDVOL0_STATUS_STATE_FAILED (0x02) + +typedef struct _RAID_VOL0_SETTINGS +{ + U16 Settings; /* 00h */ + U8 HotSparePool; /* 01h */ /* MPI_RAID_HOT_SPARE_POOL_ */ + U8 Reserved; /* 02h */ +} RAID_VOL0_SETTINGS, MPI_POINTER PTR_RAID_VOL0_SETTINGS, + RaidVol0Settings, MPI_POINTER pRaidVol0Settings; + +/* RAID Volume Page 0 VolumeSettings defines */ + +#define MPI_RAIDVOL0_SETTING_WRITE_CACHING_ENABLE (0x0001) +#define MPI_RAIDVOL0_SETTING_OFFLINE_ON_SMART (0x0002) +#define MPI_RAIDVOL0_SETTING_AUTO_CONFIGURE (0x0004) +#define MPI_RAIDVOL0_SETTING_PRIORITY_RESYNC (0x0008) +#define MPI_RAIDVOL0_SETTING_USE_PRODUCT_ID_SUFFIX (0x0010) +#define MPI_RAIDVOL0_SETTING_USE_DEFAULTS (0x8000) + +/* RAID Volume Page 0 HotSparePool defines, also used in RAID Physical Disk */ +#define MPI_RAID_HOT_SPARE_POOL_0 (0x01) +#define MPI_RAID_HOT_SPARE_POOL_1 (0x02) +#define MPI_RAID_HOT_SPARE_POOL_2 (0x04) +#define MPI_RAID_HOT_SPARE_POOL_3 (0x08) +#define MPI_RAID_HOT_SPARE_POOL_4 (0x10) +#define MPI_RAID_HOT_SPARE_POOL_5 (0x20) +#define MPI_RAID_HOT_SPARE_POOL_6 (0x40) +#define MPI_RAID_HOT_SPARE_POOL_7 (0x80) + +/* + * Host code (drivers, BIOS, utilities, etc.) should leave this define set to + * one and check Header.PageLength at runtime. + */ +#ifndef MPI_RAID_VOL_PAGE_0_PHYSDISK_MAX +#define MPI_RAID_VOL_PAGE_0_PHYSDISK_MAX (1) +#endif + +typedef struct _CONFIG_PAGE_RAID_VOL_0 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U8 VolumeID; /* 04h */ + U8 VolumeBus; /* 05h */ + U8 VolumeIOC; /* 06h */ + U8 VolumeType; /* 07h */ /* MPI_RAID_VOL_TYPE_ */ + RAID_VOL0_STATUS VolumeStatus; /* 08h */ + RAID_VOL0_SETTINGS VolumeSettings; /* 0Ch */ + U32 MaxLBA; /* 10h */ + U32 Reserved1; /* 14h */ + U32 StripeSize; /* 18h */ + U32 Reserved2; /* 1Ch */ + U32 Reserved3; /* 20h */ + U8 NumPhysDisks; /* 24h */ + U8 Reserved4; /* 25h */ + U16 Reserved5; /* 26h */ + RAID_VOL0_PHYS_DISK PhysDisk[MPI_RAID_VOL_PAGE_0_PHYSDISK_MAX];/* 28h */ +} fCONFIG_PAGE_RAID_VOL_0, MPI_POINTER PTR_CONFIG_PAGE_RAID_VOL_0, + RaidVolumePage0_t, MPI_POINTER pRaidVolumePage0_t; + +#define MPI_RAIDVOLPAGE0_PAGEVERSION (0x01) + + +/**************************************************************************** +* RAID Physical Disk Config Pages +****************************************************************************/ + +typedef struct _RAID_PHYS_DISK0_ERROR_DATA +{ + U8 ErrorCdbByte; /* 00h */ + U8 ErrorSenseKey; /* 01h */ + U16 Reserved; /* 02h */ + U16 ErrorCount; /* 04h */ + U8 ErrorASC; /* 06h */ + U8 ErrorASCQ; /* 07h */ + U16 SmartCount; /* 08h */ + U8 SmartASC; /* 0Ah */ + U8 SmartASCQ; /* 0Bh */ +} RAID_PHYS_DISK0_ERROR_DATA, MPI_POINTER PTR_RAID_PHYS_DISK0_ERROR_DATA, + RaidPhysDisk0ErrorData_t, MPI_POINTER pRaidPhysDisk0ErrorData_t; + +typedef struct _RAID_PHYS_DISK_INQUIRY_DATA +{ + U8 VendorID[8]; /* 00h */ + U8 ProductID[16]; /* 08h */ + U8 ProductRevLevel[4]; /* 18h */ + U8 Info[32]; /* 1Ch */ +} RAID_PHYS_DISK0_INQUIRY_DATA, MPI_POINTER PTR_RAID_PHYS_DISK0_INQUIRY_DATA, + RaidPhysDisk0InquiryData, MPI_POINTER pRaidPhysDisk0InquiryData; + +typedef struct _RAID_PHYS_DISK0_SETTINGS +{ + U8 SepID; /* 00h */ + U8 SepBus; /* 01h */ + U8 HotSparePool; /* 02h */ /* MPI_RAID_HOT_SPARE_POOL_ */ + U8 PhysDiskSettings; /* 03h */ +} RAID_PHYS_DISK0_SETTINGS, MPI_POINTER PTR_RAID_PHYS_DISK0_SETTINGS, + RaidPhysDiskSettings_t, MPI_POINTER pRaidPhysDiskSettings_t; + +typedef struct _RAID_PHYS_DISK0_STATUS +{ + U8 Flags; /* 00h */ + U8 State; /* 01h */ + U16 Reserved; /* 02h */ +} RAID_PHYS_DISK0_STATUS, MPI_POINTER PTR_RAID_PHYS_DISK0_STATUS, + RaidPhysDiskStatus_t, MPI_POINTER pRaidPhysDiskStatus_t; + +/* RAID Volume 2 IM Physical Disk DiskStatus flags */ + +#define MPI_PHYSDISK0_STATUS_FLAG_OUT_OF_SYNC (0x01) +#define MPI_PHYSDISK0_STATUS_FLAG_QUIESCED (0x02) + +#define MPI_PHYSDISK0_STATUS_ONLINE (0x00) +#define MPI_PHYSDISK0_STATUS_MISSING (0x01) +#define MPI_PHYSDISK0_STATUS_NOT_COMPATIBLE (0x02) +#define MPI_PHYSDISK0_STATUS_FAILED (0x03) +#define MPI_PHYSDISK0_STATUS_INITIALIZING (0x04) +#define MPI_PHYSDISK0_STATUS_OFFLINE_REQUESTED (0x05) +#define MPI_PHYSDISK0_STATUS_FAILED_REQUESTED (0x06) +#define MPI_PHYSDISK0_STATUS_OTHER_OFFLINE (0xFF) + +typedef struct _CONFIG_PAGE_RAID_PHYS_DISK_0 +{ + fCONFIG_PAGE_HEADER Header; /* 00h */ + U8 PhysDiskID; /* 04h */ + U8 PhysDiskBus; /* 05h */ + U8 PhysDiskIOC; /* 06h */ + U8 PhysDiskNum; /* 07h */ + RAID_PHYS_DISK0_SETTINGS PhysDiskSettings; /* 08h */ + U32 Reserved1; /* 0Ch */ + U32 Reserved2; /* 10h */ + U32 Reserved3; /* 14h */ + U8 DiskIdentifier[16]; /* 18h */ + RAID_PHYS_DISK0_INQUIRY_DATA InquiryData; /* 28h */ + RAID_PHYS_DISK0_STATUS PhysDiskStatus; /* 64h */ + U32 MaxLBA; /* 68h */ + RAID_PHYS_DISK0_ERROR_DATA ErrorData; /* 6Ch */ +} fCONFIG_PAGE_RAID_PHYS_DISK_0, MPI_POINTER PTR_CONFIG_PAGE_RAID_PHYS_DISK_0, + RaidPhysDiskPage0_t, MPI_POINTER pRaidPhysDiskPage0_t; + +#define MPI_RAIDPHYSDISKPAGE0_PAGEVERSION (0x00) + + +/**************************************************************************** +* LAN Config Pages +****************************************************************************/ + +typedef struct _CONFIG_PAGE_LAN_0 +{ + ConfigPageHeader_t Header; /* 00h */ + U16 TxRxModes; /* 04h */ + U16 Reserved; /* 06h */ + U32 PacketPrePad; /* 08h */ +} fCONFIG_PAGE_LAN_0, MPI_POINTER PTR_CONFIG_PAGE_LAN_0, + LANPage0_t, MPI_POINTER pLANPage0_t; + +#define MPI_LAN_PAGE0_PAGEVERSION (0x01) + +#define MPI_LAN_PAGE0_RETURN_LOOPBACK (0x0000) +#define MPI_LAN_PAGE0_SUPPRESS_LOOPBACK (0x0001) +#define MPI_LAN_PAGE0_LOOPBACK_MASK (0x0001) + +typedef struct _CONFIG_PAGE_LAN_1 +{ + ConfigPageHeader_t Header; /* 00h */ + U16 Reserved; /* 04h */ + U8 CurrentDeviceState; /* 06h */ + U8 Reserved1; /* 07h */ + U32 MinPacketSize; /* 08h */ + U32 MaxPacketSize; /* 0Ch */ + U32 HardwareAddressLow; /* 10h */ + U32 HardwareAddressHigh; /* 14h */ + U32 MaxWireSpeedLow; /* 18h */ + U32 MaxWireSpeedHigh; /* 1Ch */ + U32 BucketsRemaining; /* 20h */ + U32 MaxReplySize; /* 24h */ + U32 NegWireSpeedLow; /* 28h */ + U32 NegWireSpeedHigh; /* 2Ch */ +} fCONFIG_PAGE_LAN_1, MPI_POINTER PTR_CONFIG_PAGE_LAN_1, + LANPage1_t, MPI_POINTER pLANPage1_t; + +#define MPI_LAN_PAGE1_PAGEVERSION (0x03) + +#define MPI_LAN_PAGE1_DEV_STATE_RESET (0x00) +#define MPI_LAN_PAGE1_DEV_STATE_OPERATIONAL (0x01) + +#endif + diff --git a/xen/drivers/message/fusion/lsi/mpi_fc.h b/xen/drivers/message/fusion/lsi/mpi_fc.h new file mode 100644 index 0000000000..7873657ba3 --- /dev/null +++ b/xen/drivers/message/fusion/lsi/mpi_fc.h @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2000-2002 LSI Logic Corporation. + * + * + * Name: MPI_FC.H + * Title: MPI Fibre Channel messages and structures + * Creation Date: June 12, 2000 + * + * MPI_FC.H Version: 01.02.03 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-12-00 01.00.02 Added _MSG_FC_ABORT_REPLY structure. + * 11-02-00 01.01.01 Original release for post 1.0 work + * 12-04-00 01.01.02 Added messages for Common Transport Send and + * Primitive Send. + * 01-09-01 01.01.03 Modifed some of the new flags to have an MPI prefix + * and modified the FcPrimitiveSend flags. + * 01-25-01 01.01.04 Move InitiatorIndex in LinkServiceRsp reply to a larger + * field. + * Added FC_ABORT_TYPE_CT_SEND_REQUEST and + * FC_ABORT_TYPE_EXLINKSEND_REQUEST for FcAbort request. + * Added MPI_FC_PRIM_SEND_FLAGS_STOP_SEND. + * 02-20-01 01.01.05 Started using MPI_POINTER. + * 03-27-01 01.01.06 Added Flags field to MSG_LINK_SERVICE_BUFFER_POST_REPLY + * and defined MPI_LS_BUF_POST_REPLY_FLAG_NO_RSP_NEEDED. + * Added MPI_FC_PRIM_SEND_FLAGS_RESET_LINK define. + * Added structure offset comments. + * 04-09-01 01.01.07 Added RspLength field to MSG_LINK_SERVICE_RSP_REQUEST. + * 08-08-01 01.02.01 Original release for v1.2 work. + * 09-28-01 01.02.02 Change name of reserved field in + * MSG_LINK_SERVICE_RSP_REPLY. + * 05-31-02 01.02.03 Adding AliasIndex to FC Direct Access requests. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_FC_H +#define MPI_FC_H + + +/***************************************************************************** +* +* F C T a r g e t M o d e M e s s a g e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Link Service Buffer Post messages */ +/****************************************************************************/ + +typedef struct _MSG_LINK_SERVICE_BUFFER_POST_REQUEST +{ + U8 BufferPostFlags; /* 00h */ + U8 BufferCount; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved; /* 04h */ + U8 Reserved1; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + SGE_TRANS_SIMPLE_UNION SGL; +} MSG_LINK_SERVICE_BUFFER_POST_REQUEST, + MPI_POINTER PTR_MSG_LINK_SERVICE_BUFFER_POST_REQUEST, + LinkServiceBufferPostRequest_t, MPI_POINTER pLinkServiceBufferPostRequest_t; + +#define LINK_SERVICE_BUFFER_POST_FLAGS_PORT_MASK (0x01) + +typedef struct _WWNFORMAT +{ + U32 PortNameHigh; /* 00h */ + U32 PortNameLow; /* 04h */ + U32 NodeNameHigh; /* 08h */ + U32 NodeNameLow; /* 0Ch */ +} WWNFORMAT, + WwnFormat_t; + +/* Link Service Buffer Post Reply */ +typedef struct _MSG_LINK_SERVICE_BUFFER_POST_REPLY +{ + U8 Flags; /* 00h */ + U8 Reserved; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 PortNumber; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved2; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 TransferLength; /* 14h */ + U32 TransactionContext; /* 18h */ + U32 Rctl_Did; /* 1Ch */ + U32 Csctl_Sid; /* 20h */ + U32 Type_Fctl; /* 24h */ + U16 SeqCnt; /* 28h */ + U8 Dfctl; /* 2Ah */ + U8 SeqId; /* 2Bh */ + U16 Rxid; /* 2Ch */ + U16 Oxid; /* 2Eh */ + U32 Parameter; /* 30h */ + WWNFORMAT Wwn; /* 34h */ +} MSG_LINK_SERVICE_BUFFER_POST_REPLY, MPI_POINTER PTR_MSG_LINK_SERVICE_BUFFER_POST_REPLY, + LinkServiceBufferPostReply_t, MPI_POINTER pLinkServiceBufferPostReply_t; + +#define MPI_LS_BUF_POST_REPLY_FLAG_NO_RSP_NEEDED (0x80) + +#define MPI_FC_DID_MASK (0x00FFFFFF) +#define MPI_FC_DID_SHIFT (0) +#define MPI_FC_RCTL_MASK (0xFF000000) +#define MPI_FC_RCTL_SHIFT (24) +#define MPI_FC_SID_MASK (0x00FFFFFF) +#define MPI_FC_SID_SHIFT (0) +#define MPI_FC_CSCTL_MASK (0xFF000000) +#define MPI_FC_CSCTL_SHIFT (24) +#define MPI_FC_FCTL_MASK (0x00FFFFFF) +#define MPI_FC_FCTL_SHIFT (0) +#define MPI_FC_TYPE_MASK (0xFF000000) +#define MPI_FC_TYPE_SHIFT (24) + +/* obsolete name for the above */ +#define FCP_TARGET_DID_MASK (0x00FFFFFF) +#define FCP_TARGET_DID_SHIFT (0) +#define FCP_TARGET_RCTL_MASK (0xFF000000) +#define FCP_TARGET_RCTL_SHIFT (24) +#define FCP_TARGET_SID_MASK (0x00FFFFFF) +#define FCP_TARGET_SID_SHIFT (0) +#define FCP_TARGET_CSCTL_MASK (0xFF000000) +#define FCP_TARGET_CSCTL_SHIFT (24) +#define FCP_TARGET_FCTL_MASK (0x00FFFFFF) +#define FCP_TARGET_FCTL_SHIFT (0) +#define FCP_TARGET_TYPE_MASK (0xFF000000) +#define FCP_TARGET_TYPE_SHIFT (24) + + +/****************************************************************************/ +/* Link Service Response messages */ +/****************************************************************************/ + +typedef struct _MSG_LINK_SERVICE_RSP_REQUEST +{ + U8 RspFlags; /* 00h */ + U8 RspLength; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 Rctl_Did; /* 0Ch */ + U32 Csctl_Sid; /* 10h */ + U32 Type_Fctl; /* 14h */ + U16 SeqCnt; /* 18h */ + U8 Dfctl; /* 1Ah */ + U8 SeqId; /* 1Bh */ + U16 Rxid; /* 1Ch */ + U16 Oxid; /* 1Eh */ + U32 Parameter; /* 20h */ + SGE_SIMPLE_UNION SGL; /* 24h */ +} MSG_LINK_SERVICE_RSP_REQUEST, MPI_POINTER PTR_MSG_LINK_SERVICE_RSP_REQUEST, + LinkServiceRspRequest_t, MPI_POINTER pLinkServiceRspRequest_t; + +#define LINK_SERVICE_RSP_FLAGS_IMMEDIATE (0x80) +#define LINK_SERVICE_RSP_FLAGS_PORT_MASK (0x01) + + +/* Link Service Response Reply */ +typedef struct _MSG_LINK_SERVICE_RSP_REPLY +{ + U16 Reserved; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved_0100_InitiatorIndex; /* 06h */ /* obsolete InitiatorIndex */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 InitiatorIndex; /* 14h */ +} MSG_LINK_SERVICE_RSP_REPLY, MPI_POINTER PTR_MSG_LINK_SERVICE_RSP_REPLY, + LinkServiceRspReply_t, MPI_POINTER pLinkServiceRspReply_t; + + +/****************************************************************************/ +/* Extended Link Service Send messages */ +/****************************************************************************/ + +typedef struct _MSG_EXLINK_SERVICE_SEND_REQUEST +{ + U8 SendFlags; /* 00h */ + U8 AliasIndex; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U32 MsgFlags_Did; /* 04h */ + U32 MsgContext; /* 08h */ + U32 ElsCommandCode; /* 0Ch */ + SGE_SIMPLE_UNION SGL; /* 10h */ +} MSG_EXLINK_SERVICE_SEND_REQUEST, MPI_POINTER PTR_MSG_EXLINK_SERVICE_SEND_REQUEST, + ExLinkServiceSendRequest_t, MPI_POINTER pExLinkServiceSendRequest_t; + +#define EX_LINK_SERVICE_SEND_DID_MASK (0x00FFFFFF) +#define EX_LINK_SERVICE_SEND_DID_SHIFT (0) +#define EX_LINK_SERVICE_SEND_MSGFLAGS_MASK (0xFF000000) +#define EX_LINK_SERVICE_SEND_MSGFLAGS_SHIFT (24) + + +/* Extended Link Service Send Reply */ +typedef struct _MSG_EXLINK_SERVICE_SEND_REPLY +{ + U8 Reserved; /* 00h */ + U8 AliasIndex; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 ResponseLength; /* 14h */ +} MSG_EXLINK_SERVICE_SEND_REPLY, MPI_POINTER PTR_MSG_EXLINK_SERVICE_SEND_REPLY, + ExLinkServiceSendReply_t, MPI_POINTER pExLinkServiceSendReply_t; + +/****************************************************************************/ +/* FC Abort messages */ +/****************************************************************************/ + +typedef struct _MSG_FC_ABORT_REQUEST +{ + U8 AbortFlags; /* 00h */ + U8 AbortType; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 TransactionContextToAbort; /* 0Ch */ +} MSG_FC_ABORT_REQUEST, MPI_POINTER PTR_MSG_FC_ABORT_REQUEST, + FcAbortRequest_t, MPI_POINTER pFcAbortRequest_t; + +#define FC_ABORT_FLAG_PORT_MASK (0x01) + +#define FC_ABORT_TYPE_ALL_FC_BUFFERS (0x00) +#define FC_ABORT_TYPE_EXACT_FC_BUFFER (0x01) +#define FC_ABORT_TYPE_CT_SEND_REQUEST (0x02) +#define FC_ABORT_TYPE_EXLINKSEND_REQUEST (0x03) + +/* FC Abort Reply */ +typedef struct _MSG_FC_ABORT_REPLY +{ + U16 Reserved; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ +} MSG_FC_ABORT_REPLY, MPI_POINTER PTR_MSG_FC_ABORT_REPLY, + FcAbortReply_t, MPI_POINTER pFcAbortReply_t; + + +/****************************************************************************/ +/* FC Common Transport Send messages */ +/****************************************************************************/ + +typedef struct _MSG_FC_COMMON_TRANSPORT_SEND_REQUEST +{ + U8 SendFlags; /* 00h */ + U8 AliasIndex; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U32 MsgFlags_Did; /* 04h */ + U32 MsgContext; /* 08h */ + U16 CTCommandCode; /* 0Ch */ + U8 FsType; /* 0Eh */ + U8 Reserved1; /* 0Fh */ + SGE_SIMPLE_UNION SGL; /* 10h */ +} MSG_FC_COMMON_TRANSPORT_SEND_REQUEST, + MPI_POINTER PTR_MSG_FC_COMMON_TRANSPORT_SEND_REQUEST, + FcCommonTransportSendRequest_t, MPI_POINTER pFcCommonTransportSendRequest_t; + +#define MPI_FC_CT_SEND_DID_MASK (0x00FFFFFF) +#define MPI_FC_CT_SEND_DID_SHIFT (0) +#define MPI_FC_CT_SEND_MSGFLAGS_MASK (0xFF000000) +#define MPI_FC_CT_SEND_MSGFLAGS_SHIFT (24) + + +/* FC Common Transport Send Reply */ +typedef struct _MSG_FC_COMMON_TRANSPORT_SEND_REPLY +{ + U8 Reserved; /* 00h */ + U8 AliasIndex; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 ResponseLength; /* 14h */ +} MSG_FC_COMMON_TRANSPORT_SEND_REPLY, MPI_POINTER PTR_MSG_FC_COMMON_TRANSPORT_SEND_REPLY, + FcCommonTransportSendReply_t, MPI_POINTER pFcCommonTransportSendReply_t; + + +/****************************************************************************/ +/* FC Primitive Send messages */ +/****************************************************************************/ + +typedef struct _MSG_FC_PRIMITIVE_SEND_REQUEST +{ + U8 SendFlags; /* 00h */ + U8 Reserved; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 FcPrimitive[4]; /* 0Ch */ +} MSG_FC_PRIMITIVE_SEND_REQUEST, MPI_POINTER PTR_MSG_FC_PRIMITIVE_SEND_REQUEST, + FcPrimitiveSendRequest_t, MPI_POINTER pFcPrimitiveSendRequest_t; + +#define MPI_FC_PRIM_SEND_FLAGS_PORT_MASK (0x01) +#define MPI_FC_PRIM_SEND_FLAGS_RESET_LINK (0x04) +#define MPI_FC_PRIM_SEND_FLAGS_STOP_SEND (0x08) +#define MPI_FC_PRIM_SEND_FLAGS_SEND_ONCE (0x10) +#define MPI_FC_PRIM_SEND_FLAGS_SEND_AROUND (0x20) +#define MPI_FC_PRIM_SEND_FLAGS_UNTIL_FULL (0x40) +#define MPI_FC_PRIM_SEND_FLAGS_FOREVER (0x80) + +/* FC Primitive Send Reply */ +typedef struct _MSG_FC_PRIMITIVE_SEND_REPLY +{ + U8 SendFlags; /* 00h */ + U8 Reserved; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ +} MSG_FC_PRIMITIVE_SEND_REPLY, MPI_POINTER PTR_MSG_FC_PRIMITIVE_SEND_REPLY, + FcPrimitiveSendReply_t, MPI_POINTER pFcPrimitiveSendReply_t; + +#endif + diff --git a/xen/drivers/message/fusion/lsi/mpi_history.txt b/xen/drivers/message/fusion/lsi/mpi_history.txt new file mode 100644 index 0000000000..0deb7721e9 --- /dev/null +++ b/xen/drivers/message/fusion/lsi/mpi_history.txt @@ -0,0 +1,276 @@ + + ============================== + MPI Header File Change History + ============================== + + Copyright (c) 2000-2001 LSI Logic Corporation. + + --------------------------------------- + Header Set Release Version: 01.01.10 + Header Set Release Date: 04-09-01 + --------------------------------------- + + Filename Current version Prior version + ---------- --------------- ------------- + mpi.h 01.01.07 01.01.06 + mpi_ioc.h 01.01.07 01.01.06 + mpi_cnfg.h 01.01.11 01.01.10 + mpi_init.h 01.01.05 01.01.04 + mpi_targ.h 01.01.04 01.01.04 + mpi_fc.h 01.01.07 01.01.06 + mpi_lan.h 01.01.03 01.01.03 + mpi_raid.h 01.01.02 01.01.02 + mpi_type.h 01.01.02 01.01.02 + mpi_history.txt 01.01.09 01.01.09 + + + * Date Version Description + * -------- -------- ------------------------------------------------------ + +mpi.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH definition. + * 06-06-00 01.00.01 Update MPI_VERSION_MAJOR and MPI_VERSION_MINOR. + * 06-22-00 01.00.02 Added MPI_IOCSTATUS_LAN_ definitions. + * Removed LAN_SUSPEND function definition. + * Added MPI_MSGFLAGS_CONTINUATION_REPLY definition. + * 06-30-00 01.00.03 Added MPI_CONTEXT_REPLY_TYPE_LAN definition. + * Added MPI_GET/SET_CONTEXT_REPLY_TYPE macros. + * 07-27-00 01.00.04 Added MPI_FAULT_ definitions. + * Removed MPI_IOCSTATUS_MSG/DATA_XFER_ERROR definitions. + * Added MPI_IOCSTATUS_INTERNAL_ERROR definition. + * Added MPI_IOCSTATUS_TARGET_XFER_COUNT_MISMATCH. + * 11-02-00 01.01.01 Original release for post 1.0 work + * 12-04-00 01.01.02 Added new function codes. + * 01-09-01 01.01.03 Added more definitions to the system interface section + * Added MPI_IOCSTATUS_TARGET_STS_DATA_NOT_SENT. + * 01-25-01 01.01.04 Changed MPI_VERSION_MINOR from 0x00 to 0x01. + * 02-20-01 01.01.05 Started using MPI_POINTER. + * Added defines for MPI_DIAG_PREVENT_IOC_BOOT and + * MPI_DIAG_CLEAR_FLASH_BAD_SIG. + * Obsoleted MPI_IOCSTATUS_TARGET_FC_ defines. + * 02-27-01 01.01.06 Removed MPI_HOST_INDEX_REGISTER define. + * Added function codes for RAID. + * 04-09-01 01.01.07 Added alternate define for MPI_DOORBELL_ACTIVE, + * MPI_DOORBELL_USED, to better match the spec. + * -------------------------------------------------------------------------- + +mpi_ioc.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added _MSG_IOC_INIT_REPLY structure. + * 06-06-00 01.00.01 Added CurReplyFrameSize field to _MSG_IOC_FACTS_REPLY. + * 06-12-00 01.00.02 Added _MSG_PORT_ENABLE_REPLY structure. + * Added _MSG_EVENT_ACK_REPLY structure. + * Added _MSG_FW_DOWNLOAD_REPLY structure. + * Added _MSG_TOOLBOX_REPLY structure. + * 06-30-00 01.00.03 Added MaxLanBuckets to _PORT_FACT_REPLY structure. + * 07-27-00 01.00.04 Added _EVENT_DATA structure definitions for _SCSI, + * _LINK_STATUS, _LOOP_STATE and _LOGOUT. + * 08-11-00 01.00.05 Switched positions of MsgLength and Function fields in + * _MSG_EVENT_ACK_REPLY structure to match specification. + * 11-02-00 01.01.01 Original release for post 1.0 work + * Added a value for Manufacturer to WhoInit + * 12-04-00 01.01.02 Modified IOCFacts reply, added FWUpload messages, and + * removed toolbox message. + * 01-09-01 01.01.03 Added event enabled and disabled defines. + * Added structures for FwHeader and DataHeader. + * Added ImageType to FwUpload reply. + * 02-20-01 01.01.04 Started using MPI_POINTER. + * 02-27-01 01.01.05 Added event for RAID status change and its event data. + * Added IocNumber field to MSG_IOC_FACTS_REPLY. + * 03-27-01 01.01.06 Added defines for ProductId field of MPI_FW_HEADER. + * Added structure offset comments. + * 04-09-01 01.01.07 Added structure EVENT_DATA_EVENT_CHANGE. + * -------------------------------------------------------------------------- + +mpi_cnfg.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-08-00 01.00.02 Added _PAGEVERSION definitions for all pages. + * Added FcPhLowestVersion, FcPhHighestVersion, Reserved2 + * fields to FC_DEVICE_0 page, updated the page version. + * Changed _FREE_RUNNING_CLOCK to _PACING_TRANSFERS in + * SCSI_PORT_0, SCSI_DEVICE_0 and SCSI_DEVICE_1 pages + * and updated the page versions. + * Added _RESPONSE_ID_MASK definition to SCSI_PORT_1 + * page and updated the page version. + * Added Information field and _INFO_PARAMS_NEGOTIATED + * definitionto SCSI_DEVICE_0 page. + * 06-22-00 01.00.03 Removed batch controls from LAN_0 page and updated the + * page version. + * Added BucketsRemaining to LAN_1 page, redefined the + * state values, and updated the page version. + * Revised bus width definitions in SCSI_PORT_0, + * SCSI_DEVICE_0 and SCSI_DEVICE_1 pages. + * 06-30-00 01.00.04 Added MaxReplySize to LAN_1 page and updated the page + * version. + * Moved FC_DEVICE_0 PageAddress description to spec. + * 07-27-00 01.00.05 Corrected the SubsystemVendorID and SubsystemID field + * widths in IOC_0 page and updated the page version. + * 11-02-00 01.01.01 Original release for post 1.0 work + * Added Manufacturing pages, IO Unit Page 2, SCSI SPI + * Port Page 2, FC Port Page 4, FC Port Page 5 + * 12-04-00 01.01.03 Config page changes to match MPI rev 1.00.01. + * 12-05-00 01.01.04 Modified config page actions. + * 01-09-01 01.01.05 Added defines for page address formats. + * Data size for Manufacturing pages 2 and 3 no longer + * defined here. + * Io Unit Page 2 size is fixed at 4 adapters and some + * flags were changed. + * SCSI Port Page 2 Device Settings modified. + * New fields added to FC Port Page 0 and some flags + * cleaned up. + * Removed impedance flash from FC Port Page 1. + * Added FC Port pages 6 and 7. + * 01-25-01 01.01.06 Added MaxInitiators field to FcPortPage0. + * 01-29-01 01.01.07 Changed some defines to make them 32 character unique. + * Added some LinkType defines for FcPortPage0. + * 02-20-01 01.01.08 Started using MPI_POINTER. + * 02-27-01 01.01.09 Replaced MPI_CONFIG_PAGETYPE_SCSI_LUN with + * MPI_CONFIG_PAGETYPE_RAID_VOLUME. + * Added definitions and structures for IOC Page 2 and + * RAID Volume Page 2. + * 03-27-01 01.01.10 Added CONFIG_PAGE_FC_PORT_8 and CONFIG_PAGE_FC_PORT_9. + * CONFIG_PAGE_FC_PORT_3 now supports persistent by DID. + * Added VendorId and ProductRevLevel fields to + * RAIDVOL2_IM_PHYS_ID struct. + * Modified values for MPI_FCPORTPAGE0_FLAGS_ATTACH_ + * defines to make them compatible to MPI version 1.0. + * Added structure offset comments. + * 04-09-01 01.01.11 Added some new defines for the PageAddress field and + * removed some obsolete ones. + * Added IO Unit Page 3. + * Modified defines for Scsi Port Page 2. + * Modified RAID Volume Pages. + * -------------------------------------------------------------------------- + +mpi_init.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added SenseBufferLength to _MSG_SCSI_IO_REPLY. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-08-00 01.00.02 Added MPI_SCSI_RSP_INFO_ definitions. + * 11-02-00 01.01.01 Original release for post 1.0 work + * 12-04-00 01.01.02 Added MPI_SCSIIO_CONTROL_NO_DISCONNECT. + * 02-20-01 01.01.03 Started using MPI_POINTER. + * 03-27-01 01.01.04 Added structure offset comments. + * 04-10-01 01.01.05 Added new MsgFlag for MSG_SCSI_TASK_MGMT. + * -------------------------------------------------------------------------- + +mpi_targ.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-22-00 01.00.02 Added _MSG_TARGET_CMD_BUFFER_POST_REPLY structure. + * Corrected DECSRIPTOR typo to DESCRIPTOR. + * 11-02-00 01.01.01 Original release for post 1.0 work + * Modified target mode to use IoIndex instead of + * HostIndex and IocIndex. Added Alias. + * 01-09-01 01.01.02 Added defines for TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER + * and TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER. + * 02-20-01 01.01.03 Started using MPI_POINTER. + * Added structures for MPI_TARGET_SCSI_SPI_CMD_BUFFER and + * MPI_TARGET_FCP_CMD_BUFFER. + * 03-27-01 01.01.04 Added structure offset comments. + * -------------------------------------------------------------------------- + +mpi_fc.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-12-00 01.00.02 Added _MSG_FC_ABORT_REPLY structure. + * 11-02-00 01.01.01 Original release for post 1.0 work + * 12-04-00 01.01.02 Added messages for Common Transport Send and + * Primitive Send. + * 01-09-01 01.01.03 Modifed some of the new flags to have an MPI prefix + * and modified the FcPrimitiveSend flags. + * 01-25-01 01.01.04 Move InitiatorIndex in LinkServiceRsp reply to a larger + * field. + * Added FC_ABORT_TYPE_CT_SEND_REQUEST and + * FC_ABORT_TYPE_EXLINKSEND_REQUEST for FcAbort request. + * Added MPI_FC_PRIM_SEND_FLAGS_STOP_SEND. + * 02-20-01 01.01.05 Started using MPI_POINTER. + * 03-27-01 01.01.06 Added Flags field to MSG_LINK_SERVICE_BUFFER_POST_REPLY + * and defined MPI_LS_BUF_POST_REPLY_FLAG_NO_RSP_NEEDED. + * Added MPI_FC_PRIM_SEND_FLAGS_RESET_LINK define. + * Added structure offset comments. + * 04-09-01 01.01.07 Added RspLength field to MSG_LINK_SERVICE_RSP_REQUEST. + * -------------------------------------------------------------------------- + +mpi_lan.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added LANStatus field to _MSG_LAN_SEND_REPLY. + * Added LANStatus field to _MSG_LAN_RECEIVE_POST_REPLY. + * Moved ListCount field in _MSG_LAN_RECEIVE_POST_REPLY. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-12-00 01.00.02 Added MPI_ to BUCKETSTATUS_ definitions. + * 06-22-00 01.00.03 Major changes to match new LAN definition in 1.0 spec. + * 06-30-00 01.00.04 Added Context Reply definitions per revised proposal. + * Changed transaction context usage to bucket/buffer. + * 07-05-00 01.00.05 Removed LAN_RECEIVE_POST_BUCKET_CONTEXT_MASK definition + * to lan private header file + * 11-02-00 01.01.01 Original release for post 1.0 work + * 02-20-01 01.01.02 Started using MPI_POINTER. + * 03-27-01 01.01.03 Added structure offset comments. + * -------------------------------------------------------------------------- + +mpi_raid.h + * 02-27-01 01.01.01 Original release for this file. + * 03-27-01 01.01.02 Added structure offset comments. + * -------------------------------------------------------------------------- + +mpi_type.h + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 11-02-00 01.01.01 Original release for post 1.0 work + * 02-20-01 01.01.02 Added define and ifdef for MPI_POINTER. + * -------------------------------------------------------------------------- + +mpi_history.txt Parts list history + +Filename 01.01.10 +---------- -------- +mpi.h 01.01.07 +mpi_ioc.h 01.01.07 +mpi_cnfg.h 01.01.11 +mpi_init.h 01.01.05 +mpi_targ.h 01.01.04 +mpi_fc.h 01.01.07 +mpi_lan.h 01.01.03 +mpi_raid.h 01.01.02 +mpi_type.h 01.01.02 + +Filename 01.01.09 01.01.08 01.01.07 01.01.06 01.01.05 01.01.04 +---------- -------- -------- -------- -------- -------- -------- +mpi.h 01.01.06 01.01.06 01.01.05 01.01.04 01.01.04 01.01.03 +mpi_ioc.h 01.01.06 01.01.05 01.01.04 01.01.03 01.01.03 01.01.03 +mpi_cnfg.h 01.01.10 01.01.09 01.01.08 01.01.07 01.01.06 01.01.05 +mpi_init.h 01.01.04 01.01.03 01.01.03 01.01.02 01.01.02 01.01.02 +mpi_targ.h 01.01.04 01.01.03 01.01.03 01.01.02 01.01.02 01.01.02 +mpi_fc.h 01.01.06 01.01.05 01.01.05 01.01.04 01.01.04 01.01.03 +mpi_lan.h 01.01.03 01.01.02 01.01.02 01.01.01 01.01.01 01.01.01 +mpi_raid.h 01.01.02 01.01.01 +mpi_type.h 01.01.02 01.01.02 01.01.02 01.01.01 01.01.01 01.01.01 + +Filename 01.01.03 01.01.02 01.01.01 01.00.07 01.00.06 01.00.05 +---------- -------- -------- -------- -------- -------- -------- +mpi.h 01.01.02 01.01.02 01.01.01 01.00.04 01.00.04 01.00.03 +mpi_ioc.h 01.01.02 01.01.02 01.01.01 01.00.05 01.00.04 01.00.03 +mpi_cnfg.h 01.01.04 01.01.03 01.01.01 01.00.05 01.00.05 01.00.04 +mpi_init.h 01.01.02 01.01.02 01.01.01 01.00.02 01.00.02 01.00.02 +mpi_targ.h 01.01.01 01.01.01 01.01.01 01.00.02 01.00.02 01.00.02 +mpi_fc.h 01.01.02 01.01.02 01.01.01 01.00.02 01.00.02 01.00.02 +mpi_lan.h 01.01.01 01.01.01 01.01.01 01.00.05 01.00.05 01.00.05 +mpi_type.h 01.01.01 01.01.01 01.01.01 01.00.01 01.00.01 01.00.01 + +Filename 01.00.04 01.00.03 01.00.02 01.00.01 00.10.02 00.10.01 +---------- -------- -------- -------- -------- -------- -------- +mpi.h 01.00.02 01.00.01 01.00.01 01.00.01 00.10.02 00.10.01 +mpi_ioc.h 01.00.02 01.00.02 01.00.01 01.00.01 00.10.02 00.10.01 +mpi_cnfg.h 01.00.03 01.00.02 01.00.02 01.00.01 00.10.01 00.10.01 +mpi_init.h 01.00.02 01.00.02 01.00.02 01.00.01 00.10.02 00.10.01 +mpi_targ.h 01.00.02 01.00.01 01.00.01 01.00.01 00.10.01 00.10.01 +mpi_fc.h 01.00.02 01.00.02 01.00.01 01.00.01 00.10.01 00.10.01 +mpi_lan.h 01.00.03 01.00.02 01.00.01 01.00.01 00.10.02 00.10.01 +mpi_type.h 01.00.01 01.00.01 01.00.01 01.00.01 00.10.01 00.10.01 + + + * -------------------------------------------------------------------------- + diff --git a/xen/drivers/message/fusion/lsi/mpi_init.h b/xen/drivers/message/fusion/lsi/mpi_init.h new file mode 100644 index 0000000000..4b3256e4e7 --- /dev/null +++ b/xen/drivers/message/fusion/lsi/mpi_init.h @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2000-2002 LSI Logic Corporation. + * + * + * Name: MPI_INIT.H + * Title: MPI initiator mode messages and structures + * Creation Date: June 8, 2000 + * + * MPI_INIT.H Version: 01.02.05 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added SenseBufferLength to _MSG_SCSI_IO_REPLY. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-08-00 01.00.02 Added MPI_SCSI_RSP_INFO_ definitions. + * 11-02-00 01.01.01 Original release for post 1.0 work. + * 12-04-00 01.01.02 Added MPI_SCSIIO_CONTROL_NO_DISCONNECT. + * 02-20-01 01.01.03 Started using MPI_POINTER. + * 03-27-01 01.01.04 Added structure offset comments. + * 04-10-01 01.01.05 Added new MsgFlag for MSG_SCSI_TASK_MGMT. + * 08-08-01 01.02.01 Original release for v1.2 work. + * 08-29-01 01.02.02 Added MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET. + * Added MPI_SCSI_STATE_QUEUE_TAG_REJECTED for + * MSG_SCSI_IO_REPLY. + * 09-28-01 01.02.03 Added structures and defines for SCSI Enclosure + * Processor messages. + * 10-04-01 01.02.04 Added defines for SEP request Action field. + * 05-31-02 01.02.05 Added MPI_SCSIIO_MSGFLGS_CMD_DETERMINES_DATA_DIR define + * for SCSI IO requests. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_INIT_H +#define MPI_INIT_H + + +/***************************************************************************** +* +* S C S I I n i t i a t o r M e s s a g e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* SCSI IO messages and assocaited structures */ +/****************************************************************************/ + +typedef struct _MSG_SCSI_IO_REQUEST +{ + U8 TargetID; /* 00h */ + U8 Bus; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 CDBLength; /* 04h */ + U8 SenseBufferLength; /* 05h */ + U8 Reserved; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 LUN[8]; /* 0Ch */ + U32 Control; /* 14h */ + U8 CDB[16]; /* 18h */ + U32 DataLength; /* 28h */ + U32 SenseBufferLowAddr; /* 2Ch */ + SGE_IO_UNION SGL; /* 30h */ +} MSG_SCSI_IO_REQUEST, MPI_POINTER PTR_MSG_SCSI_IO_REQUEST, + SCSIIORequest_t, MPI_POINTER pSCSIIORequest_t; + + +/* SCSI IO MsgFlags bits */ + +#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH (0x01) +#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_32 (0x00) +#define MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64 (0x01) +#define MPI_SCSIIO_MSGFLGS_SENSE_LOCATION (0x02) +#define MPI_SCSIIO_MSGFLGS_SENSE_LOC_HOST (0x00) +#define MPI_SCSIIO_MSGFLGS_SENSE_LOC_IOC (0x02) +#define MPI_SCSIIO_MSGFLGS_CMD_DETERMINES_DATA_DIR (0x04) + +/* SCSI IO LUN fields */ + +#define MPI_SCSIIO_LUN_FIRST_LEVEL_ADDRESSING (0x0000FFFF) +#define MPI_SCSIIO_LUN_SECOND_LEVEL_ADDRESSING (0xFFFF0000) +#define MPI_SCSIIO_LUN_THIRD_LEVEL_ADDRESSING (0x0000FFFF) +#define MPI_SCSIIO_LUN_FOURTH_LEVEL_ADDRESSING (0xFFFF0000) +#define MPI_SCSIIO_LUN_LEVEL_1_WORD (0xFF00) +#define MPI_SCSIIO_LUN_LEVEL_1_DWORD (0x0000FF00) + +/* SCSI IO Control bits */ + +#define MPI_SCSIIO_CONTROL_DATADIRECTION_MASK (0x03000000) +#define MPI_SCSIIO_CONTROL_NODATATRANSFER (0x00000000) +#define MPI_SCSIIO_CONTROL_WRITE (0x01000000) +#define MPI_SCSIIO_CONTROL_READ (0x02000000) + +#define MPI_SCSIIO_CONTROL_ADDCDBLEN_MASK (0x3C000000) +#define MPI_SCSIIO_CONTROL_ADDCDBLEN_SHIFT (26) + +#define MPI_SCSIIO_CONTROL_TASKATTRIBUTE_MASK (0x00000700) +#define MPI_SCSIIO_CONTROL_SIMPLEQ (0x00000000) +#define MPI_SCSIIO_CONTROL_HEADOFQ (0x00000100) +#define MPI_SCSIIO_CONTROL_ORDEREDQ (0x00000200) +#define MPI_SCSIIO_CONTROL_ACAQ (0x00000400) +#define MPI_SCSIIO_CONTROL_UNTAGGED (0x00000500) +#define MPI_SCSIIO_CONTROL_NO_DISCONNECT (0x00000700) + +#define MPI_SCSIIO_CONTROL_TASKMANAGE_MASK (0x00FF0000) +#define MPI_SCSIIO_CONTROL_OBSOLETE (0x00800000) +#define MPI_SCSIIO_CONTROL_CLEAR_ACA_RSV (0x00400000) +#define MPI_SCSIIO_CONTROL_TARGET_RESET (0x00200000) +#define MPI_SCSIIO_CONTROL_LUN_RESET_RSV (0x00100000) +#define MPI_SCSIIO_CONTROL_RESERVED (0x00080000) +#define MPI_SCSIIO_CONTROL_CLR_TASK_SET_RSV (0x00040000) +#define MPI_SCSIIO_CONTROL_ABORT_TASK_SET (0x00020000) +#define MPI_SCSIIO_CONTROL_RESERVED2 (0x00010000) + + +/* SCSI IO reply structure */ +typedef struct _MSG_SCSI_IO_REPLY +{ + U8 TargetID; /* 00h */ + U8 Bus; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 CDBLength; /* 04h */ + U8 SenseBufferLength; /* 05h */ + U8 Reserved; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 SCSIStatus; /* 0Ch */ + U8 SCSIState; /* 0Dh */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 TransferCount; /* 14h */ + U32 SenseCount; /* 18h */ + U32 ResponseInfo; /* 1Ch */ +} MSG_SCSI_IO_REPLY, MPI_POINTER PTR_MSG_SCSI_IO_REPLY, + SCSIIOReply_t, MPI_POINTER pSCSIIOReply_t; + + +/* SCSI IO Reply SCSIStatus values (SAM-2 status codes) */ + +#define MPI_SCSI_STATUS_SUCCESS (0x00) +#define MPI_SCSI_STATUS_CHECK_CONDITION (0x02) +#define MPI_SCSI_STATUS_CONDITION_MET (0x04) +#define MPI_SCSI_STATUS_BUSY (0x08) +#define MPI_SCSI_STATUS_INTERMEDIATE (0x10) +#define MPI_SCSI_STATUS_INTERMEDIATE_CONDMET (0x14) +#define MPI_SCSI_STATUS_RESERVATION_CONFLICT (0x18) +#define MPI_SCSI_STATUS_COMMAND_TERMINATED (0x22) +#define MPI_SCSI_STATUS_TASK_SET_FULL (0x28) +#define MPI_SCSI_STATUS_ACA_ACTIVE (0x30) + + +/* SCSI IO Reply SCSIState values */ + +#define MPI_SCSI_STATE_AUTOSENSE_VALID (0x01) +#define MPI_SCSI_STATE_AUTOSENSE_FAILED (0x02) +#define MPI_SCSI_STATE_NO_SCSI_STATUS (0x04) +#define MPI_SCSI_STATE_TERMINATED (0x08) +#define MPI_SCSI_STATE_RESPONSE_INFO_VALID (0x10) +#define MPI_SCSI_STATE_QUEUE_TAG_REJECTED (0x20) + +/* SCSI IO Reply ResponseInfo values */ +/* (FCP-1 RSP_CODE values and SPI-3 Packetized Failure codes) */ + +#define MPI_SCSI_RSP_INFO_FUNCTION_COMPLETE (0x00000000) +#define MPI_SCSI_RSP_INFO_FCP_BURST_LEN_ERROR (0x01000000) +#define MPI_SCSI_RSP_INFO_CMND_FIELDS_INVALID (0x02000000) +#define MPI_SCSI_RSP_INFO_FCP_DATA_RO_ERROR (0x03000000) +#define MPI_SCSI_RSP_INFO_TASK_MGMT_UNSUPPORTED (0x04000000) +#define MPI_SCSI_RSP_INFO_TASK_MGMT_FAILED (0x05000000) +#define MPI_SCSI_RSP_INFO_SPI_LQ_INVALID_TYPE (0x06000000) + + +/****************************************************************************/ +/* SCSI Task Management messages */ +/****************************************************************************/ + +typedef struct _MSG_SCSI_TASK_MGMT +{ + U8 TargetID; /* 00h */ + U8 Bus; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved; /* 04h */ + U8 TaskType; /* 05h */ + U8 Reserved1; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 LUN[8]; /* 0Ch */ + U32 Reserved2[7]; /* 14h */ + U32 TaskMsgContext; /* 30h */ +} MSG_SCSI_TASK_MGMT, MPI_POINTER PTR_SCSI_TASK_MGMT, + SCSITaskMgmt_t, MPI_POINTER pSCSITaskMgmt_t; + +/* TaskType values */ + +#define MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK (0x01) +#define MPI_SCSITASKMGMT_TASKTYPE_ABRT_TASK_SET (0x02) +#define MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET (0x03) +#define MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS (0x04) +#define MPI_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET (0x05) + +/* MsgFlags bits */ +#define MPI_SCSITASKMGMT_MSGFLAGS_TARGET_RESET_OPTION (0x00) +#define MPI_SCSITASKMGMT_MSGFLAGS_LIP_RESET_OPTION (0x02) +#define MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION (0x04) + +/* SCSI Task Management Reply */ +typedef struct _MSG_SCSI_TASK_MGMT_REPLY +{ + U8 TargetID; /* 00h */ + U8 Bus; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved; /* 04h */ + U8 TaskType; /* 05h */ + U8 Reserved1; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 Reserved2[2]; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 TerminationCount; /* 14h */ +} MSG_SCSI_TASK_MGMT_REPLY, MPI_POINTER PTR_MSG_SCSI_TASK_MGMT_REPLY, + SCSITaskMgmtReply_t, MPI_POINTER pSCSITaskMgmtReply_t; + + +/****************************************************************************/ +/* SCSI Enclosure Processor messages */ +/****************************************************************************/ + +typedef struct _MSG_SEP_REQUEST +{ + U8 TargetID; /* 00h */ + U8 Bus; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 Action; /* 04h */ + U8 Reserved1; /* 05h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 SlotStatus; /* 0Ch */ +} MSG_SEP_REQUEST, MPI_POINTER PTR_MSG_SEP_REQUEST, + SEPRequest_t, MPI_POINTER pSEPRequest_t; + +/* Action defines */ +#define MPI_SEP_REQ_ACTION_WRITE_STATUS (0x00) +#define MPI_SEP_REQ_ACTION_READ_STATUS (0x01) + +/* SlotStatus bits for MSG_SEP_REQUEST */ +#define MPI_SEP_REQ_SLOTSTATUS_NO_ERROR (0x00000001) +#define MPI_SEP_REQ_SLOTSTATUS_DEV_FAULTY (0x00000002) +#define MPI_SEP_REQ_SLOTSTATUS_DEV_REBUILDING (0x00000004) +#define MPI_SEP_REQ_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008) +#define MPI_SEP_REQ_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010) +#define MPI_SEP_REQ_SLOTSTATUS_PARITY_CHECK (0x00000020) +#define MPI_SEP_REQ_SLOTSTATUS_PREDICTED_FAULT (0x00000040) +#define MPI_SEP_REQ_SLOTSTATUS_UNCONFIGURED (0x00000080) +#define MPI_SEP_REQ_SLOTSTATUS_HOT_SPARE (0x00000100) +#define MPI_SEP_REQ_SLOTSTATUS_REBUILD_STOPPED (0x00000200) +#define MPI_SEP_REQ_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000) +#define MPI_SEP_REQ_SLOTSTATUS_REQUEST_REMOVE (0x00040000) +#define MPI_SEP_REQ_SLOTSTATUS_REQUEST_INSERT (0x00080000) +#define MPI_SEP_REQ_SLOTSTATUS_DO_NOT_MOVE (0x00400000) +#define MPI_SEP_REQ_SLOTSTATUS_B_ENABLE_BYPASS (0x04000000) +#define MPI_SEP_REQ_SLOTSTATUS_A_ENABLE_BYPASS (0x08000000) +#define MPI_SEP_REQ_SLOTSTATUS_DEV_OFF (0x10000000) +#define MPI_SEP_REQ_SLOTSTATUS_SWAP_RESET (0x80000000) + + +typedef struct _MSG_SEP_REPLY +{ + U8 TargetID; /* 00h */ + U8 Bus; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 Action; /* 04h */ + U8 Reserved1; /* 05h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 SlotStatus; /* 14h */ +} MSG_SEP_REPLY, MPI_POINTER PTR_MSG_SEP_REPLY, + SEPReply_t, MPI_POINTER pSEPReply_t; + +/* SlotStatus bits for MSG_SEP_REPLY */ +#define MPI_SEP_REPLY_SLOTSTATUS_NO_ERROR (0x00000001) +#define MPI_SEP_REPLY_SLOTSTATUS_DEV_FAULTY (0x00000002) +#define MPI_SEP_REPLY_SLOTSTATUS_DEV_REBUILDING (0x00000004) +#define MPI_SEP_REPLY_SLOTSTATUS_IN_FAILED_ARRAY (0x00000008) +#define MPI_SEP_REPLY_SLOTSTATUS_IN_CRITICAL_ARRAY (0x00000010) +#define MPI_SEP_REPLY_SLOTSTATUS_PARITY_CHECK (0x00000020) +#define MPI_SEP_REPLY_SLOTSTATUS_PREDICTED_FAULT (0x00000040) +#define MPI_SEP_REPLY_SLOTSTATUS_UNCONFIGURED (0x00000080) +#define MPI_SEP_REPLY_SLOTSTATUS_HOT_SPARE (0x00000100) +#define MPI_SEP_REPLY_SLOTSTATUS_REBUILD_STOPPED (0x00000200) +#define MPI_SEP_REPLY_SLOTSTATUS_REPORT (0x00010000) +#define MPI_SEP_REPLY_SLOTSTATUS_IDENTIFY_REQUEST (0x00020000) +#define MPI_SEP_REPLY_SLOTSTATUS_REMOVE_READY (0x00040000) +#define MPI_SEP_REPLY_SLOTSTATUS_INSERT_READY (0x00080000) +#define MPI_SEP_REPLY_SLOTSTATUS_DO_NOT_REMOVE (0x00400000) +#define MPI_SEP_REPLY_SLOTSTATUS_B_BYPASS_ENABLED (0x01000000) +#define MPI_SEP_REPLY_SLOTSTATUS_A_BYPASS_ENABLED (0x02000000) +#define MPI_SEP_REPLY_SLOTSTATUS_B_ENABLE_BYPASS (0x04000000) +#define MPI_SEP_REPLY_SLOTSTATUS_A_ENABLE_BYPASS (0x08000000) +#define MPI_SEP_REPLY_SLOTSTATUS_DEV_OFF (0x10000000) +#define MPI_SEP_REPLY_SLOTSTATUS_FAULT_SENSED (0x40000000) +#define MPI_SEP_REPLY_SLOTSTATUS_SWAPPED (0x80000000) + +#endif diff --git a/xen/drivers/message/fusion/lsi/mpi_ioc.h b/xen/drivers/message/fusion/lsi/mpi_ioc.h new file mode 100644 index 0000000000..c58a4f4b83 --- /dev/null +++ b/xen/drivers/message/fusion/lsi/mpi_ioc.h @@ -0,0 +1,698 @@ +/* + * Copyright (c) 2000-2002 LSI Logic Corporation. + * + * + * Name: MPI_IOC.H + * Title: MPI IOC, Port, Event, FW Download, and FW Upload messages + * Creation Date: August 11, 2000 + * + * MPI_IOC.H Version: 01.02.06 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added _MSG_IOC_INIT_REPLY structure. + * 06-06-00 01.00.01 Added CurReplyFrameSize field to _MSG_IOC_FACTS_REPLY. + * 06-12-00 01.00.02 Added _MSG_PORT_ENABLE_REPLY structure. + * Added _MSG_EVENT_ACK_REPLY structure. + * Added _MSG_FW_DOWNLOAD_REPLY structure. + * Added _MSG_TOOLBOX_REPLY structure. + * 06-30-00 01.00.03 Added MaxLanBuckets to _PORT_FACT_REPLY structure. + * 07-27-00 01.00.04 Added _EVENT_DATA structure definitions for _SCSI, + * _LINK_STATUS, _LOOP_STATE and _LOGOUT. + * 08-11-00 01.00.05 Switched positions of MsgLength and Function fields in + * _MSG_EVENT_ACK_REPLY structure to match specification. + * 11-02-00 01.01.01 Original release for post 1.0 work. + * Added a value for Manufacturer to WhoInit. + * 12-04-00 01.01.02 Modified IOCFacts reply, added FWUpload messages, and + * removed toolbox message. + * 01-09-01 01.01.03 Added event enabled and disabled defines. + * Added structures for FwHeader and DataHeader. + * Added ImageType to FwUpload reply. + * 02-20-01 01.01.04 Started using MPI_POINTER. + * 02-27-01 01.01.05 Added event for RAID status change and its event data. + * Added IocNumber field to MSG_IOC_FACTS_REPLY. + * 03-27-01 01.01.06 Added defines for ProductId field of MPI_FW_HEADER. + * Added structure offset comments. + * 04-09-01 01.01.07 Added structure EVENT_DATA_EVENT_CHANGE. + * 08-08-01 01.02.01 Original release for v1.2 work. + * New format for FWVersion and ProductId in + * MSG_IOC_FACTS_REPLY and MPI_FW_HEADER. + * 08-31-01 01.02.02 Addded event MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE and + * related structure and defines. + * Added event MPI_EVENT_ON_BUS_TIMER_EXPIRED. + * Added MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE. + * Replaced a reserved field in MSG_IOC_FACTS_REPLY with + * IOCExceptions and changed DataImageSize to reserved. + * Added MPI_FW_DOWNLOAD_ITYPE_NVSTORE_DATA and + * MPI_FW_UPLOAD_ITYPE_NVDATA. + * 09-28-01 01.02.03 Modified Event Data for Integrated RAID. + * 11-01-01 01.02.04 Added defines for MPI_EXT_IMAGE_HEADER ImageType field. + * 03-14-02 01.02.05 Added HeaderVersion field to MSG_IOC_FACTS_REPLY. + * 05-31-02 01.02.06 Added define for + * MPI_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID. + * Added AliasIndex to EVENT_DATA_LOGOUT structure. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_IOC_H +#define MPI_IOC_H + + +/***************************************************************************** +* +* I O C M e s s a g e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* IOCInit message */ +/****************************************************************************/ + +typedef struct _MSG_IOC_INIT +{ + U8 WhoInit; /* 00h */ + U8 Reserved; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 Flags; /* 04h */ + U8 MaxDevices; /* 05h */ + U8 MaxBuses; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 ReplyFrameSize; /* 0Ch */ + U8 Reserved1[2]; /* 0Eh */ + U32 HostMfaHighAddr; /* 10h */ + U32 SenseBufferHighAddr; /* 14h */ +} MSG_IOC_INIT, MPI_POINTER PTR_MSG_IOC_INIT, + IOCInit_t, MPI_POINTER pIOCInit_t; + +/* WhoInit values */ +#define MPI_WHOINIT_NO_ONE (0x00) +#define MPI_WHOINIT_SYSTEM_BIOS (0x01) +#define MPI_WHOINIT_ROM_BIOS (0x02) +#define MPI_WHOINIT_PCI_PEER (0x03) +#define MPI_WHOINIT_HOST_DRIVER (0x04) +#define MPI_WHOINIT_MANUFACTURER (0x05) + +/* Flags values */ +#define MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE (0x01) + +typedef struct _MSG_IOC_INIT_REPLY +{ + U8 WhoInit; /* 00h */ + U8 Reserved; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 Flags; /* 04h */ + U8 MaxDevices; /* 05h */ + U8 MaxBuses; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved2; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ +} MSG_IOC_INIT_REPLY, MPI_POINTER PTR_MSG_IOC_INIT_REPLY, + IOCInitReply_t, MPI_POINTER pIOCInitReply_t; + + + +/****************************************************************************/ +/* IOC Facts message */ +/****************************************************************************/ + +typedef struct _MSG_IOC_FACTS +{ + U8 Reserved[2]; /* 00h */ + U8 ChainOffset; /* 01h */ + U8 Function; /* 02h */ + U8 Reserved1[3]; /* 03h */ + U8 MsgFlags; /* 04h */ + U32 MsgContext; /* 08h */ +} MSG_IOC_FACTS, MPI_POINTER PTR_IOC_FACTS, + IOCFacts_t, MPI_POINTER pIOCFacts_t; + +typedef struct _MPI_FW_VERSION_STRUCT +{ + U8 Dev; /* 00h */ + U8 Unit; /* 01h */ + U8 Minor; /* 02h */ + U8 Major; /* 03h */ +} MPI_FW_VERSION_STRUCT; + +typedef union _MPI_FW_VERSION +{ + MPI_FW_VERSION_STRUCT Struct; + U32 Word; +} MPI_FW_VERSION; + +/* IOC Facts Reply */ +typedef struct _MSG_IOC_FACTS_REPLY +{ + U16 MsgVersion; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 HeaderVersion; /* 04h */ + U8 IOCNumber; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 IOCExceptions; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U8 MaxChainDepth; /* 14h */ + U8 WhoInit; /* 15h */ + U8 BlockSize; /* 16h */ + U8 Flags; /* 17h */ + U16 ReplyQueueDepth; /* 18h */ + U16 RequestFrameSize; /* 1Ah */ + U16 Reserved_0101_FWVersion; /* 1Ch */ /* obsolete 16-bit FWVersion */ + U16 ProductID; /* 1Eh */ + U32 CurrentHostMfaHighAddr; /* 20h */ + U16 GlobalCredits; /* 24h */ + U8 NumberOfPorts; /* 26h */ + U8 EventState; /* 27h */ + U32 CurrentSenseBufferHighAddr; /* 28h */ + U16 CurReplyFrameSize; /* 2Ch */ + U8 MaxDevices; /* 2Eh */ + U8 MaxBuses; /* 2Fh */ + U32 FWImageSize; /* 30h */ + U32 Reserved4; /* 34h */ + MPI_FW_VERSION FWVersion; /* 38h */ +} MSG_IOC_FACTS_REPLY, MPI_POINTER PTR_MSG_IOC_FACTS_REPLY, + IOCFactsReply_t, MPI_POINTER pIOCFactsReply_t; + +#define MPI_IOCFACTS_MSGVERSION_MAJOR_MASK (0xFF00) +#define MPI_IOCFACTS_MSGVERSION_MINOR_MASK (0x00FF) + +#define MPI_IOCFACTS_HEADERVERSION_UNIT_MASK (0xFF00) +#define MPI_IOCFACTS_HEADERVERSION_DEV_MASK (0x00FF) + +#define MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL (0x0001) +#define MPI_IOCFACTS_EXCEPT_RAID_CONFIG_INVALID (0x0002) + +#define MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT (0x01) + +#define MPI_IOCFACTS_EVENTSTATE_DISABLED (0x00) +#define MPI_IOCFACTS_EVENTSTATE_ENABLED (0x01) + + + +/***************************************************************************** +* +* P o r t M e s s a g e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Port Facts message and Reply */ +/****************************************************************************/ + +typedef struct _MSG_PORT_FACTS +{ + U8 Reserved[2]; /* 00h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved1[2]; /* 04h */ + U8 PortNumber; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ +} MSG_PORT_FACTS, MPI_POINTER PTR_MSG_PORT_FACTS, + PortFacts_t, MPI_POINTER pPortFacts_t; + +typedef struct _MSG_PORT_FACTS_REPLY +{ + U16 Reserved; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 PortNumber; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved2; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U8 Reserved3; /* 14h */ + U8 PortType; /* 15h */ + U16 MaxDevices; /* 16h */ + U16 PortSCSIID; /* 18h */ + U16 ProtocolFlags; /* 1Ah */ + U16 MaxPostedCmdBuffers; /* 1Ch */ + U16 MaxPersistentIDs; /* 1Eh */ + U16 MaxLanBuckets; /* 20h */ + U16 Reserved4; /* 22h */ + U32 Reserved5; /* 24h */ +} MSG_PORT_FACTS_REPLY, MPI_POINTER PTR_MSG_PORT_FACTS_REPLY, + PortFactsReply_t, MPI_POINTER pPortFactsReply_t; + + +/* PortTypes values */ + +#define MPI_PORTFACTS_PORTTYPE_INACTIVE (0x00) +#define MPI_PORTFACTS_PORTTYPE_SCSI (0x01) +#define MPI_PORTFACTS_PORTTYPE_FC (0x10) + +/* ProtocolFlags values */ + +#define MPI_PORTFACTS_PROTOCOL_LOGBUSADDR (0x01) +#define MPI_PORTFACTS_PROTOCOL_LAN (0x02) +#define MPI_PORTFACTS_PROTOCOL_TARGET (0x04) +#define MPI_PORTFACTS_PROTOCOL_INITIATOR (0x08) + + +/****************************************************************************/ +/* Port Enable Message */ +/****************************************************************************/ + +typedef struct _MSG_PORT_ENABLE +{ + U8 Reserved[2]; /* 00h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved1[2]; /* 04h */ + U8 PortNumber; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ +} MSG_PORT_ENABLE, MPI_POINTER PTR_MSG_PORT_ENABLE, + PortEnable_t, MPI_POINTER pPortEnable_t; + +typedef struct _MSG_PORT_ENABLE_REPLY +{ + U8 Reserved[2]; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved1[2]; /* 04h */ + U8 PortNumber; /* 05h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved2; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ +} MSG_PORT_ENABLE_REPLY, MPI_POINTER PTR_MSG_PORT_ENABLE_REPLY, + PortEnableReply_t, MPI_POINTER pPortEnableReply_t; + + +/***************************************************************************** +* +* E v e n t M e s s a g e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Event Notification messages */ +/****************************************************************************/ + +typedef struct _MSG_EVENT_NOTIFY +{ + U8 Switch; /* 00h */ + U8 Reserved; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved1[3]; /* 04h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ +} MSG_EVENT_NOTIFY, MPI_POINTER PTR_MSG_EVENT_NOTIFY, + EventNotification_t, MPI_POINTER pEventNotification_t; + +/* Event Notification Reply */ + +typedef struct _MSG_EVENT_NOTIFY_REPLY +{ + U16 EventDataLength; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved1[2]; /* 04h */ + U8 AckRequired; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 Reserved2[2]; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 Event; /* 14h */ + U32 EventContext; /* 18h */ + U32 Data[1]; /* 1Ch */ +} MSG_EVENT_NOTIFY_REPLY, MPI_POINTER PTR_MSG_EVENT_NOTIFY_REPLY, + EventNotificationReply_t, MPI_POINTER pEventNotificationReply_t; + +/* Event Acknowledge */ + +typedef struct _MSG_EVENT_ACK +{ + U8 Reserved[2]; /* 00h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved1[3]; /* 04h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 Event; /* 0Ch */ + U32 EventContext; /* 10h */ +} MSG_EVENT_ACK, MPI_POINTER PTR_MSG_EVENT_ACK, + EventAck_t, MPI_POINTER pEventAck_t; + +typedef struct _MSG_EVENT_ACK_REPLY +{ + U8 Reserved[2]; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved1[3]; /* 04h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved2; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ +} MSG_EVENT_ACK_REPLY, MPI_POINTER PTR_MSG_EVENT_ACK_REPLY, + EventAckReply_t, MPI_POINTER pEventAckReply_t; + +/* Switch */ + +#define MPI_EVENT_NOTIFICATION_SWITCH_OFF (0x00) +#define MPI_EVENT_NOTIFICATION_SWITCH_ON (0x01) + +/* Event */ + +#define MPI_EVENT_NONE (0x00000000) +#define MPI_EVENT_LOG_DATA (0x00000001) +#define MPI_EVENT_STATE_CHANGE (0x00000002) +#define MPI_EVENT_UNIT_ATTENTION (0x00000003) +#define MPI_EVENT_IOC_BUS_RESET (0x00000004) +#define MPI_EVENT_EXT_BUS_RESET (0x00000005) +#define MPI_EVENT_RESCAN (0x00000006) +#define MPI_EVENT_LINK_STATUS_CHANGE (0x00000007) +#define MPI_EVENT_LOOP_STATE_CHANGE (0x00000008) +#define MPI_EVENT_LOGOUT (0x00000009) +#define MPI_EVENT_EVENT_CHANGE (0x0000000A) +#define MPI_EVENT_INTEGRATED_RAID (0x0000000B) +#define MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE (0x0000000C) +#define MPI_EVENT_ON_BUS_TIMER_EXPIRED (0x0000000D) + +/* AckRequired field values */ + +#define MPI_EVENT_NOTIFICATION_ACK_NOT_REQUIRED (0x00) +#define MPI_EVENT_NOTIFICATION_ACK_REQUIRED (0x01) + +/* EventChange Event data */ + +typedef struct _EVENT_DATA_EVENT_CHANGE +{ + U8 EventState; /* 00h */ + U8 Reserved; /* 01h */ + U16 Reserved1; /* 02h */ +} EVENT_DATA_EVENT_CHANGE, MPI_POINTER PTR_EVENT_DATA_EVENT_CHANGE, + EventDataEventChange_t, MPI_POINTER pEventDataEventChange_t; + +/* SCSI Event data for Port, Bus and Device forms */ + +typedef struct _EVENT_DATA_SCSI +{ + U8 TargetID; /* 00h */ + U8 BusPort; /* 01h */ + U16 Reserved; /* 02h */ +} EVENT_DATA_SCSI, MPI_POINTER PTR_EVENT_DATA_SCSI, + EventDataScsi_t, MPI_POINTER pEventDataScsi_t; + +/* SCSI Device Status Change Event data */ + +typedef struct _EVENT_DATA_SCSI_DEVICE_STATUS_CHANGE +{ + U8 TargetID; /* 00h */ + U8 Bus; /* 01h */ + U8 ReasonCode; /* 02h */ + U8 LUN; /* 03h */ + U8 ASC; /* 04h */ + U8 ASCQ; /* 05h */ + U16 Reserved; /* 06h */ +} EVENT_DATA_SCSI_DEVICE_STATUS_CHANGE, + MPI_POINTER PTR_EVENT_DATA_SCSI_DEVICE_STATUS_CHANGE, + MpiEventDataScsiDeviceStatusChange_t, + MPI_POINTER pMpiEventDataScsiDeviceStatusChange_t; + +/* MPI SCSI Device Status Change Event data ReasonCode values */ +#define MPI_EVENT_SCSI_DEV_STAT_RC_ADDED (0x03) +#define MPI_EVENT_SCSI_DEV_STAT_RC_NOT_RESPONDING (0x04) +#define MPI_EVENT_SCSI_DEV_STAT_RC_SMART_DATA (0x05) + +/* MPI Link Status Change Event data */ + +typedef struct _EVENT_DATA_LINK_STATUS +{ + U8 State; /* 00h */ + U8 Reserved; /* 01h */ + U16 Reserved1; /* 02h */ + U8 Reserved2; /* 04h */ + U8 Port; /* 05h */ + U16 Reserved3; /* 06h */ +} EVENT_DATA_LINK_STATUS, MPI_POINTER PTR_EVENT_DATA_LINK_STATUS, + EventDataLinkStatus_t, MPI_POINTER pEventDataLinkStatus_t; + +#define MPI_EVENT_LINK_STATUS_FAILURE (0x00000000) +#define MPI_EVENT_LINK_STATUS_ACTIVE (0x00000001) + +/* MPI Loop State Change Event data */ + +typedef struct _EVENT_DATA_LOOP_STATE +{ + U8 Character4; /* 00h */ + U8 Character3; /* 01h */ + U8 Type; /* 02h */ + U8 Reserved; /* 03h */ + U8 Reserved1; /* 04h */ + U8 Port; /* 05h */ + U16 Reserved2; /* 06h */ +} EVENT_DATA_LOOP_STATE, MPI_POINTER PTR_EVENT_DATA_LOOP_STATE, + EventDataLoopState_t, MPI_POINTER pEventDataLoopState_t; + +#define MPI_EVENT_LOOP_STATE_CHANGE_LIP (0x0001) +#define MPI_EVENT_LOOP_STATE_CHANGE_LPE (0x0002) +#define MPI_EVENT_LOOP_STATE_CHANGE_LPB (0x0003) + +/* MPI LOGOUT Event data */ + +typedef struct _EVENT_DATA_LOGOUT +{ + U32 NPortID; /* 00h */ + U8 AliasIndex; /* 04h */ + U8 Port; /* 05h */ + U16 Reserved1; /* 06h */ +} EVENT_DATA_LOGOUT, MPI_POINTER PTR_EVENT_DATA_LOGOUT, + EventDataLogout_t, MPI_POINTER pEventDataLogout_t; + +#define MPI_EVENT_LOGOUT_ALL_ALIASES (0xFF) + + +/* MPI Integrated RAID Event data */ + +typedef struct _EVENT_DATA_RAID +{ + U8 VolumeID; /* 00h */ + U8 VolumeBus; /* 01h */ + U8 ReasonCode; /* 02h */ + U8 PhysDiskNum; /* 03h */ + U8 ASC; /* 04h */ + U8 ASCQ; /* 05h */ + U16 Reserved; /* 06h */ + U32 SettingsStatus; /* 08h */ +} EVENT_DATA_RAID, MPI_POINTER PTR_EVENT_DATA_RAID, + MpiEventDataRaid_t, MPI_POINTER pMpiEventDataRaid_t; + +/* MPI Integrated RAID Event data ReasonCode values */ +#define MPI_EVENT_RAID_RC_VOLUME_CREATED (0x00) +#define MPI_EVENT_RAID_RC_VOLUME_DELETED (0x01) +#define MPI_EVENT_RAID_RC_VOLUME_SETTINGS_CHANGED (0x02) +#define MPI_EVENT_RAID_RC_VOLUME_STATUS_CHANGED (0x03) +#define MPI_EVENT_RAID_RC_VOLUME_PHYSDISK_CHANGED (0x04) +#define MPI_EVENT_RAID_RC_PHYSDISK_CREATED (0x05) +#define MPI_EVENT_RAID_RC_PHYSDISK_DELETED (0x06) +#define MPI_EVENT_RAID_RC_PHYSDISK_SETTINGS_CHANGED (0x07) +#define MPI_EVENT_RAID_RC_PHYSDISK_STATUS_CHANGED (0x08) +#define MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED (0x09) +#define MPI_EVENT_RAID_RC_SMART_DATA (0x0A) +#define MPI_EVENT_RAID_RC_REPLACE_ACTION_STARTED (0x0B) + + +/***************************************************************************** +* +* F i r m w a r e L o a d M e s s a g e s +* +*****************************************************************************/ + +/****************************************************************************/ +/* Firmware Download message and associated structures */ +/****************************************************************************/ + +typedef struct _MSG_FW_DOWNLOAD +{ + U8 ImageType; /* 00h */ + U8 Reserved; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved1[3]; /* 04h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + SGE_MPI_UNION SGL; /* 0Ch */ +} MSG_FW_DOWNLOAD, MPI_POINTER PTR_MSG_FW_DOWNLOAD, + FWDownload_t, MPI_POINTER pFWDownload_t; + +#define MPI_FW_DOWNLOAD_ITYPE_RESERVED (0x00) +#define MPI_FW_DOWNLOAD_ITYPE_FW (0x01) +#define MPI_FW_DOWNLOAD_ITYPE_BIOS (0x02) +#define MPI_FW_DOWNLOAD_ITYPE_NVDATA (0x03) + + +typedef struct _FWDownloadTCSGE +{ + U8 Reserved; /* 00h */ + U8 ContextSize; /* 01h */ + U8 DetailsLength; /* 02h */ + U8 Flags; /* 03h */ + U32 Reserved_0100_Checksum; /* 04h */ /* obsolete Checksum */ + U32 ImageOffset; /* 08h */ + U32 ImageSize; /* 0Ch */ +} FW_DOWNLOAD_TCSGE, MPI_POINTER PTR_FW_DOWNLOAD_TCSGE, + FWDownloadTCSGE_t, MPI_POINTER pFWDownloadTCSGE_t; + +/* Firmware Download reply */ +typedef struct _MSG_FW_DOWNLOAD_REPLY +{ + U8 ImageType; /* 00h */ + U8 Reserved; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved1[3]; /* 04h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved2; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ +} MSG_FW_DOWNLOAD_REPLY, MPI_POINTER PTR_MSG_FW_DOWNLOAD_REPLY, + FWDownloadReply_t, MPI_POINTER pFWDownloadReply_t; + + +/****************************************************************************/ +/* Firmware Upload message and associated structures */ +/****************************************************************************/ + +typedef struct _MSG_FW_UPLOAD +{ + U8 ImageType; /* 00h */ + U8 Reserved; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved1[3]; /* 04h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + SGE_MPI_UNION SGL; /* 0Ch */ +} MSG_FW_UPLOAD, MPI_POINTER PTR_MSG_FW_UPLOAD, + FWUpload_t, MPI_POINTER pFWUpload_t; + +#define MPI_FW_UPLOAD_ITYPE_FW_IOC_MEM (0x00) +#define MPI_FW_UPLOAD_ITYPE_FW_FLASH (0x01) +#define MPI_FW_UPLOAD_ITYPE_BIOS_FLASH (0x02) +#define MPI_FW_UPLOAD_ITYPE_NVDATA (0x03) + +typedef struct _FWUploadTCSGE +{ + U8 Reserved; /* 00h */ + U8 ContextSize; /* 01h */ + U8 DetailsLength; /* 02h */ + U8 Flags; /* 03h */ + U32 Reserved1; /* 04h */ + U32 ImageOffset; /* 08h */ + U32 ImageSize; /* 0Ch */ +} FW_UPLOAD_TCSGE, MPI_POINTER PTR_FW_UPLOAD_TCSGE, + FWUploadTCSGE_t, MPI_POINTER pFWUploadTCSGE_t; + +/* Firmware Upload reply */ +typedef struct _MSG_FW_UPLOAD_REPLY +{ + U8 ImageType; /* 00h */ + U8 Reserved; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved1[3]; /* 04h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved2; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 ActualImageSize; /* 14h */ +} MSG_FW_UPLOAD_REPLY, MPI_POINTER PTR_MSG_FW_UPLOAD_REPLY, + FWUploadReply_t, MPI_POINTER pFWUploadReply_t; + + +typedef struct _MPI_FW_HEADER +{ + U32 ArmBranchInstruction0; /* 00h */ + U32 Signature0; /* 04h */ + U32 Signature1; /* 08h */ + U32 Signature2; /* 0Ch */ + U32 ArmBranchInstruction1; /* 10h */ + U32 ArmBranchInstruction2; /* 14h */ + U32 Reserved; /* 18h */ + U32 Checksum; /* 1Ch */ + U16 VendorId; /* 20h */ + U16 ProductId; /* 22h */ + MPI_FW_VERSION FWVersion; /* 24h */ + U32 SeqCodeVersion; /* 28h */ + U32 ImageSize; /* 2Ch */ + U32 NextImageHeaderOffset; /* 30h */ + U32 LoadStartAddress; /* 34h */ + U32 IopResetVectorValue; /* 38h */ + U32 IopResetRegAddr; /* 3Ch */ + U32 VersionNameWhat; /* 40h */ + U8 VersionName[32]; /* 44h */ + U32 VendorNameWhat; /* 64h */ + U8 VendorName[32]; /* 68h */ +} MPI_FW_HEADER, MPI_POINTER PTR_MPI_FW_HEADER, + MpiFwHeader_t, MPI_POINTER pMpiFwHeader_t; + +#define MPI_FW_HEADER_WHAT_SIGNATURE (0x29232840) + +/* defines for using the ProductId field */ +#define MPI_FW_HEADER_PID_TYPE_MASK (0xF000) +#define MPI_FW_HEADER_PID_TYPE_SCSI (0x0000) +#define MPI_FW_HEADER_PID_TYPE_FC (0x1000) + +#define MPI_FW_HEADER_PID_PROD_MASK (0x0F00) +#define MPI_FW_HEADER_PID_PROD_INITIATOR_SCSI (0x0100) +#define MPI_FW_HEADER_PID_PROD_TARGET_INITIATOR_SCSI (0x0200) +#define MPI_FW_HEADER_PID_PROD_TARGET_SCSI (0x0300) +#define MPI_FW_HEADER_PID_PROD_IM_SCSI (0x0400) +#define MPI_FW_HEADER_PID_PROD_IS_SCSI (0x0500) +#define MPI_FW_HEADER_PID_PROD_CTX_SCSI (0x0600) + +#define MPI_FW_HEADER_PID_FAMILY_MASK (0x00FF) +#define MPI_FW_HEADER_PID_FAMILY_1030A0_SCSI (0x0001) +#define MPI_FW_HEADER_PID_FAMILY_1030B0_SCSI (0x0002) +#define MPI_FW_HEADER_PID_FAMILY_1030B1_SCSI (0x0003) +#define MPI_FW_HEADER_PID_FAMILY_1030C0_SCSI (0x0004) +#define MPI_FW_HEADER_PID_FAMILY_1020A0_SCSI (0x0005) +#define MPI_FW_HEADER_PID_FAMILY_1020B0_SCSI (0x0006) +#define MPI_FW_HEADER_PID_FAMILY_1020B1_SCSI (0x0007) +#define MPI_FW_HEADER_PID_FAMILY_1020C0_SCSI (0x0008) +#define MPI_FW_HEADER_PID_FAMILY_1035A0_SCSI (0x0009) +#define MPI_FW_HEADER_PID_FAMILY_1035B0_SCSI (0x000A) +#define MPI_FW_HEADER_PID_FAMILY_909_FC (0x0000) +#define MPI_FW_HEADER_PID_FAMILY_919_FC (0x0001) +#define MPI_FW_HEADER_PID_FAMILY_919X_FC (0x0002) + +typedef struct _MPI_EXT_IMAGE_HEADER +{ + U8 ImageType; /* 00h */ + U8 Reserved; /* 01h */ + U16 Reserved1; /* 02h */ + U32 Checksum; /* 04h */ + U32 ImageSize; /* 08h */ + U32 NextImageHeaderOffset; /* 0Ch */ + U32 LoadStartAddress; /* 10h */ + U32 Reserved2; /* 14h */ +} MPI_EXT_IMAGE_HEADER, MPI_POINTER PTR_MPI_EXT_IMAGE_HEADER, + MpiExtImageHeader_t, MPI_POINTER pMpiExtImageHeader_t; + +/* defines for the ImageType field */ +#define MPI_EXT_IMAGE_TYPE_UNSPECIFIED (0x00) +#define MPI_EXT_IMAGE_TYPE_FW (0x01) +#define MPI_EXT_IMAGE_TYPE_NVDATA (0x03) + +#endif diff --git a/xen/drivers/message/fusion/lsi/mpi_lan.h b/xen/drivers/message/fusion/lsi/mpi_lan.h new file mode 100644 index 0000000000..739c5cd717 --- /dev/null +++ b/xen/drivers/message/fusion/lsi/mpi_lan.h @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2000-2002 LSI Logic Corporation. + * + * + * Name: MPI_LAN.H + * Title: MPI LAN messages and structures + * Creation Date: June 30, 2000 + * + * MPI_LAN.H Version: 01.02.01 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 05-24-00 00.10.02 Added LANStatus field to _MSG_LAN_SEND_REPLY. + * Added LANStatus field to _MSG_LAN_RECEIVE_POST_REPLY. + * Moved ListCount field in _MSG_LAN_RECEIVE_POST_REPLY. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-12-00 01.00.02 Added MPI_ to BUCKETSTATUS_ definitions. + * 06-22-00 01.00.03 Major changes to match new LAN definition in 1.0 spec. + * 06-30-00 01.00.04 Added Context Reply definitions per revised proposal. + * Changed transaction context usage to bucket/buffer. + * 07-05-00 01.00.05 Removed LAN_RECEIVE_POST_BUCKET_CONTEXT_MASK definition + * to lan private header file + * 11-02-00 01.01.01 Original release for post 1.0 work + * 02-20-01 01.01.02 Started using MPI_POINTER. + * 03-27-01 01.01.03 Added structure offset comments. + * 08-08-01 01.02.01 Original release for v1.2 work. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_LAN_H +#define MPI_LAN_H + + +/****************************************************************************** +* +* L A N M e s s a g e s +* +*******************************************************************************/ + +/* LANSend messages */ + +typedef struct _MSG_LAN_SEND_REQUEST +{ + U16 Reserved; /* 00h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 PortNumber; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + SGE_MPI_UNION SG_List[1]; /* 0Ch */ +} MSG_LAN_SEND_REQUEST, MPI_POINTER PTR_MSG_LAN_SEND_REQUEST, + LANSendRequest_t, MPI_POINTER pLANSendRequest_t; + + +typedef struct _MSG_LAN_SEND_REPLY +{ + U16 Reserved; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved2; /* 04h */ + U8 NumberOfContexts; /* 05h */ + U8 PortNumber; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 BufferContext; /* 14h */ +} MSG_LAN_SEND_REPLY, MPI_POINTER PTR_MSG_LAN_SEND_REPLY, + LANSendReply_t, MPI_POINTER pLANSendReply_t; + + +/* LANReceivePost */ + +typedef struct _MSG_LAN_RECEIVE_POST_REQUEST +{ + U16 Reserved; /* 00h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 PortNumber; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 BucketCount; /* 0Ch */ + SGE_MPI_UNION SG_List[1]; /* 10h */ +} MSG_LAN_RECEIVE_POST_REQUEST, MPI_POINTER PTR_MSG_LAN_RECEIVE_POST_REQUEST, + LANReceivePostRequest_t, MPI_POINTER pLANReceivePostRequest_t; + + +typedef struct _MSG_LAN_RECEIVE_POST_REPLY +{ + U16 Reserved; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 Reserved2; /* 04h */ + U8 NumberOfContexts; /* 05h */ + U8 PortNumber; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 BucketsRemaining; /* 14h */ + U32 PacketOffset; /* 18h */ + U32 PacketLength; /* 1Ch */ + U32 BucketContext[1]; /* 20h */ +} MSG_LAN_RECEIVE_POST_REPLY, MPI_POINTER PTR_MSG_LAN_RECEIVE_POST_REPLY, + LANReceivePostReply_t, MPI_POINTER pLANReceivePostReply_t; + + +/* LANReset */ + +typedef struct _MSG_LAN_RESET_REQUEST +{ + U16 Reserved; /* 00h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 PortNumber; /* 05h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ +} MSG_LAN_RESET_REQUEST, MPI_POINTER PTR_MSG_LAN_RESET_REQUEST, + LANResetRequest_t, MPI_POINTER pLANResetRequest_t; + + +typedef struct _MSG_LAN_RESET_REPLY +{ + U16 Reserved; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 PortNumber; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ +} MSG_LAN_RESET_REPLY, MPI_POINTER PTR_MSG_LAN_RESET_REPLY, + LANResetReply_t, MPI_POINTER pLANResetReply_t; + + +/****************************************************************************/ +/* LAN Context Reply defines and macros */ +/****************************************************************************/ + +#define LAN_REPLY_PACKET_LENGTH_MASK (0x0000FFFF) +#define LAN_REPLY_PACKET_LENGTH_SHIFT (0) +#define LAN_REPLY_BUCKET_CONTEXT_MASK (0x07FF0000) +#define LAN_REPLY_BUCKET_CONTEXT_SHIFT (16) +#define LAN_REPLY_BUFFER_CONTEXT_MASK (0x07FFFFFF) +#define LAN_REPLY_BUFFER_CONTEXT_SHIFT (0) +#define LAN_REPLY_FORM_MASK (0x18000000) +#define LAN_REPLY_FORM_RECEIVE_SINGLE (0x00) +#define LAN_REPLY_FORM_RECEIVE_MULTIPLE (0x01) +#define LAN_REPLY_FORM_SEND_SINGLE (0x02) +#define LAN_REPLY_FORM_MESSAGE_CONTEXT (0x03) +#define LAN_REPLY_FORM_SHIFT (27) + +#define GET_LAN_PACKET_LENGTH(x) (((x) & LAN_REPLY_PACKET_LENGTH_MASK) \ + >> LAN_REPLY_PACKET_LENGTH_SHIFT) + +#define SET_LAN_PACKET_LENGTH(x, lth) \ + ((x) = ((x) & ~LAN_REPLY_PACKET_LENGTH_MASK) | \ + (((lth) << LAN_REPLY_PACKET_LENGTH_SHIFT) & \ + LAN_REPLY_PACKET_LENGTH_MASK)) + +#define GET_LAN_BUCKET_CONTEXT(x) (((x) & LAN_REPLY_BUCKET_CONTEXT_MASK) \ + >> LAN_REPLY_BUCKET_CONTEXT_SHIFT) + +#define SET_LAN_BUCKET_CONTEXT(x, ctx) \ + ((x) = ((x) & ~LAN_REPLY_BUCKET_CONTEXT_MASK) | \ + (((ctx) << LAN_REPLY_BUCKET_CONTEXT_SHIFT) & \ + LAN_REPLY_BUCKET_CONTEXT_MASK)) + +#define GET_LAN_BUFFER_CONTEXT(x) (((x) & LAN_REPLY_BUFFER_CONTEXT_MASK) \ + >> LAN_REPLY_BUFFER_CONTEXT_SHIFT) + +#define SET_LAN_BUFFER_CONTEXT(x, ctx) \ + ((x) = ((x) & ~LAN_REPLY_BUFFER_CONTEXT_MASK) | \ + (((ctx) << LAN_REPLY_BUFFER_CONTEXT_SHIFT) & \ + LAN_REPLY_BUFFER_CONTEXT_MASK)) + +#define GET_LAN_FORM(x) (((x) & LAN_REPLY_FORM_MASK) \ + >> LAN_REPLY_FORM_SHIFT) + +#define SET_LAN_FORM(x, frm) \ + ((x) = ((x) & ~LAN_REPLY_FORM_MASK) | \ + (((frm) << LAN_REPLY_FORM_SHIFT) & \ + LAN_REPLY_FORM_MASK)) + + +/****************************************************************************/ +/* LAN Current Device State defines */ +/****************************************************************************/ + +#define MPI_LAN_DEVICE_STATE_RESET (0x00) +#define MPI_LAN_DEVICE_STATE_OPERATIONAL (0x01) + + +/****************************************************************************/ +/* LAN Loopback defines */ +/****************************************************************************/ + +#define MPI_LAN_TX_MODES_ENABLE_LOOPBACK_SUPPRESSION (0x01) + +#endif + diff --git a/xen/drivers/message/fusion/lsi/mpi_raid.h b/xen/drivers/message/fusion/lsi/mpi_raid.h new file mode 100644 index 0000000000..c120cfae0f --- /dev/null +++ b/xen/drivers/message/fusion/lsi/mpi_raid.h @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2001-2002 LSI Logic Corporation. + * + * + * Name: MPI_RAID.H + * Title: MPI RAID message and structures + * Creation Date: February 27, 2001 + * + * MPI_RAID.H Version: 01.02.07 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 02-27-01 01.01.01 Original release for this file. + * 03-27-01 01.01.02 Added structure offset comments. + * 08-08-01 01.02.01 Original release for v1.2 work. + * 09-28-01 01.02.02 Major rework for MPI v1.2 Integrated RAID changes. + * 10-04-01 01.02.03 Added ActionData defines for + * MPI_RAID_ACTION_DELETE_VOLUME action. + * 11-01-01 01.02.04 Added define for MPI_RAID_ACTION_ADATA_DO_NOT_SYNC. + * 03-14-02 01.02.05 Added define for MPI_RAID_ACTION_ADATA_LOW_LEVEL_INIT. + * 05-07-02 01.02.06 Added define for MPI_RAID_ACTION_ACTIVATE_VOLUME, + * MPI_RAID_ACTION_INACTIVATE_VOLUME, and + * MPI_RAID_ACTION_ADATA_INACTIVATE_ALL. + * 07-12-02 01.02.07 Added structures for Mailbox request and reply. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_RAID_H +#define MPI_RAID_H + + +/****************************************************************************** +* +* R A I D M e s s a g e s +* +*******************************************************************************/ + + +/****************************************************************************/ +/* RAID Volume Request */ +/****************************************************************************/ + +typedef struct _MSG_RAID_ACTION +{ + U8 Action; /* 00h */ + U8 Reserved1; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 VolumeID; /* 04h */ + U8 VolumeBus; /* 05h */ + U8 PhysDiskNum; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 Reserved2; /* 0Ch */ + U32 ActionDataWord; /* 10h */ + SGE_SIMPLE_UNION ActionDataSGE; /* 14h */ +} MSG_RAID_ACTION_REQUEST, MPI_POINTER PTR_MSG_RAID_ACTION_REQUEST, + MpiRaidActionRequest_t , MPI_POINTER pMpiRaidActionRequest_t; + + +/* RAID Action request Action values */ + +#define MPI_RAID_ACTION_STATUS (0x00) +#define MPI_RAID_ACTION_INDICATOR_STRUCT (0x01) +#define MPI_RAID_ACTION_CREATE_VOLUME (0x02) +#define MPI_RAID_ACTION_DELETE_VOLUME (0x03) +#define MPI_RAID_ACTION_DISABLE_VOLUME (0x04) +#define MPI_RAID_ACTION_ENABLE_VOLUME (0x05) +#define MPI_RAID_ACTION_QUIESCE_PHYS_IO (0x06) +#define MPI_RAID_ACTION_ENABLE_PHYS_IO (0x07) +#define MPI_RAID_ACTION_CHANGE_VOLUME_SETTINGS (0x08) +#define MPI_RAID_ACTION_PHYSDISK_OFFLINE (0x0A) +#define MPI_RAID_ACTION_PHYSDISK_ONLINE (0x0B) +#define MPI_RAID_ACTION_CHANGE_PHYSDISK_SETTINGS (0x0C) +#define MPI_RAID_ACTION_CREATE_PHYSDISK (0x0D) +#define MPI_RAID_ACTION_DELETE_PHYSDISK (0x0E) +#define MPI_RAID_ACTION_FAIL_PHYSDISK (0x0F) +#define MPI_RAID_ACTION_REPLACE_PHYSDISK (0x10) +#define MPI_RAID_ACTION_ACTIVATE_VOLUME (0x11) +#define MPI_RAID_ACTION_INACTIVATE_VOLUME (0x12) + +/* ActionDataWord defines for use with MPI_RAID_ACTION_CREATE_VOLUME action */ +#define MPI_RAID_ACTION_ADATA_DO_NOT_SYNC (0x00000001) +#define MPI_RAID_ACTION_ADATA_LOW_LEVEL_INIT (0x00000002) + +/* ActionDataWord defines for use with MPI_RAID_ACTION_DELETE_VOLUME action */ +#define MPI_RAID_ACTION_ADATA_KEEP_PHYS_DISKS (0x00000000) +#define MPI_RAID_ACTION_ADATA_DEL_PHYS_DISKS (0x00000001) + +/* ActionDataWord defines for use with MPI_RAID_ACTION_ACTIVATE_VOLUME action */ +#define MPI_RAID_ACTION_ADATA_INACTIVATE_ALL (0x00000001) + + +/* RAID Action reply message */ + +typedef struct _MSG_RAID_ACTION_REPLY +{ + U8 Action; /* 00h */ + U8 Reserved; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 VolumeID; /* 04h */ + U8 VolumeBus; /* 05h */ + U8 PhysDiskNum; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 ActionStatus; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 VolumeStatus; /* 14h */ + U32 ActionData; /* 18h */ +} MSG_RAID_ACTION_REPLY, MPI_POINTER PTR_MSG_RAID_ACTION_REPLY, + MpiRaidActionReply_t, MPI_POINTER pMpiRaidActionReply_t; + + +/* RAID Volume reply ActionStatus values */ + +#define MPI_RAID_ACTION_ASTATUS_SUCCESS (0x0000) +#define MPI_RAID_ACTION_ASTATUS_INVALID_ACTION (0x0001) +#define MPI_RAID_ACTION_ASTATUS_FAILURE (0x0002) +#define MPI_RAID_ACTION_ASTATUS_IN_PROGRESS (0x0003) + + +/* RAID Volume reply RAID Volume Indicator structure */ + +typedef struct _MPI_RAID_VOL_INDICATOR +{ + U64 TotalBlocks; /* 00h */ + U64 BlocksRemaining; /* 08h */ +} MPI_RAID_VOL_INDICATOR, MPI_POINTER PTR_MPI_RAID_VOL_INDICATOR, + MpiRaidVolIndicator_t, MPI_POINTER pMpiRaidVolIndicator_t; + + +/****************************************************************************/ +/* SCSI IO RAID Passthrough Request */ +/****************************************************************************/ + +typedef struct _MSG_SCSI_IO_RAID_PT_REQUEST +{ + U8 PhysDiskNum; /* 00h */ + U8 Reserved1; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 CDBLength; /* 04h */ + U8 SenseBufferLength; /* 05h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 LUN[8]; /* 0Ch */ + U32 Control; /* 14h */ + U8 CDB[16]; /* 18h */ + U32 DataLength; /* 28h */ + U32 SenseBufferLowAddr; /* 2Ch */ + SGE_IO_UNION SGL; /* 30h */ +} MSG_SCSI_IO_RAID_PT_REQUEST, MPI_POINTER PTR_MSG_SCSI_IO_RAID_PT_REQUEST, + SCSIIORaidPassthroughRequest_t, MPI_POINTER pSCSIIORaidPassthroughRequest_t; + + +/* SCSI IO RAID Passthrough reply structure */ + +typedef struct _MSG_SCSI_IO_RAID_PT_REPLY +{ + U8 PhysDiskNum; /* 00h */ + U8 Reserved1; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 CDBLength; /* 04h */ + U8 SenseBufferLength; /* 05h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 SCSIStatus; /* 0Ch */ + U8 SCSIState; /* 0Dh */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 TransferCount; /* 14h */ + U32 SenseCount; /* 18h */ + U32 ResponseInfo; /* 1Ch */ +} MSG_SCSI_IO_RAID_PT_REPLY, MPI_POINTER PTR_MSG_SCSI_IO_RAID_PT_REPLY, + SCSIIORaidPassthroughReply_t, MPI_POINTER pSCSIIORaidPassthroughReply_t; + + +/****************************************************************************/ +/* Mailbox reqeust structure */ +/****************************************************************************/ + +typedef struct _MSG_MAILBOX_REQUEST +{ + U16 Reserved1; + U8 ChainOffset; + U8 Function; + U16 Reserved2; + U8 Reserved3; + U8 MsgFlags; + U8 Command[10]; + U16 Reserved4; + SGE_IO_UNION SGL; +} MSG_MAILBOX_REQUEST, MPI_POINTER PTR_MSG_MAILBOX_REQUEST, + MailboxRequest_t, MPI_POINTER pMailboxRequest_t; + + +/* Mailbox reply structure */ +typedef struct _MSG_MAILBOX_REPLY +{ + U16 Reserved1; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved2; /* 04h */ + U8 Reserved3; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 MailboxStatus; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 Reserved4; /* 14h */ +} MSG_MAILBOX_REPLY, MPI_POINTER PTR_MSG_MAILBOX_REPLY, + MailboxReply_t, MPI_POINTER pMailboxReply_t; + +#endif + + + diff --git a/xen/drivers/message/fusion/lsi/mpi_targ.h b/xen/drivers/message/fusion/lsi/mpi_targ.h new file mode 100644 index 0000000000..adc9b36d95 --- /dev/null +++ b/xen/drivers/message/fusion/lsi/mpi_targ.h @@ -0,0 +1,429 @@ +/* + * Copyright (c) 2000-2002 LSI Logic Corporation. + * + * + * Name: MPI_TARG.H + * Title: MPI Target mode messages and structures + * Creation Date: June 22, 2000 + * + * MPI_TARG.H Version: 01.02.07 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 06-22-00 01.00.02 Added _MSG_TARGET_CMD_BUFFER_POST_REPLY structure. + * Corrected DECSRIPTOR typo to DESCRIPTOR. + * 11-02-00 01.01.01 Original release for post 1.0 work + * Modified target mode to use IoIndex instead of + * HostIndex and IocIndex. Added Alias. + * 01-09-01 01.01.02 Added defines for TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER + * and TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER. + * 02-20-01 01.01.03 Started using MPI_POINTER. + * Added structures for MPI_TARGET_SCSI_SPI_CMD_BUFFER and + * MPI_TARGET_FCP_CMD_BUFFER. + * 03-27-01 01.01.04 Added structure offset comments. + * 08-08-01 01.02.01 Original release for v1.2 work. + * 09-28-01 01.02.02 Added structure for MPI_TARGET_SCSI_SPI_STATUS_IU. + * Added PriorityReason field to some replies and + * defined more PriorityReason codes. + * Added some defines for to support previous version + * of MPI. + * 10-04-01 01.02.03 Added PriorityReason to MSG_TARGET_ERROR_REPLY. + * 11-01-01 01.02.04 Added define for TARGET_STATUS_SEND_FLAGS_HIGH_PRIORITY. + * 03-14-02 01.02.05 Modified MPI_TARGET_FCP_RSP_BUFFER to get the proper + * byte ordering. + * 05-31-02 01.02.06 Modified TARGET_MODE_REPLY_ALIAS_MASK to only include + * one bit. + * Added AliasIndex field to MPI_TARGET_FCP_CMD_BUFFER. + * 09-16-02 01.02.07 Added flags for confirmed completion. + * Added PRIORITY_REASON_TARGET_BUSY. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_TARG_H +#define MPI_TARG_H + + +/****************************************************************************** +* +* S C S I T a r g e t M e s s a g e s +* +*******************************************************************************/ + +typedef struct _CMD_BUFFER_DESCRIPTOR +{ + U16 IoIndex; /* 00h */ + U16 Reserved; /* 02h */ + union /* 04h */ + { + U32 PhysicalAddress32; + U64 PhysicalAddress64; + } u; +} CMD_BUFFER_DESCRIPTOR, MPI_POINTER PTR_CMD_BUFFER_DESCRIPTOR, + CmdBufferDescriptor_t, MPI_POINTER pCmdBufferDescriptor_t; + + +/****************************************************************************/ +/* Target Command Buffer Post Request */ +/****************************************************************************/ + +typedef struct _MSG_TARGET_CMD_BUFFER_POST_REQUEST +{ + U8 BufferPostFlags; /* 00h */ + U8 BufferCount; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U8 BufferLength; /* 04h */ + U8 Reserved; /* 05h */ + U8 Reserved1; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + CMD_BUFFER_DESCRIPTOR Buffer[1]; /* 0Ch */ +} MSG_TARGET_CMD_BUFFER_POST_REQUEST, MPI_POINTER PTR_MSG_TARGET_CMD_BUFFER_POST_REQUEST, + TargetCmdBufferPostRequest_t, MPI_POINTER pTargetCmdBufferPostRequest_t; + +#define CMD_BUFFER_POST_FLAGS_PORT_MASK (0x01) +#define CMD_BUFFER_POST_FLAGS_ADDR_MODE_MASK (0x80) +#define CMD_BUFFER_POST_FLAGS_ADDR_MODE_32 (0) +#define CMD_BUFFER_POST_FLAGS_ADDR_MODE_64 (1) +#define CMD_BUFFER_POST_FLAGS_64_BIT_ADDR (0x80) + +#define CMD_BUFFER_POST_IO_INDEX_MASK (0x00003FFF) +#define CMD_BUFFER_POST_IO_INDEX_MASK_0100 (0x000003FF) /* obsolete */ + + +typedef struct _MSG_TARGET_CMD_BUFFER_POST_REPLY +{ + U8 BufferPostFlags; /* 00h */ + U8 BufferCount; /* 01h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U8 BufferLength; /* 04h */ + U8 Reserved; /* 05h */ + U8 Reserved1; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved2; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ +} MSG_TARGET_CMD_BUFFER_POST_REPLY, MPI_POINTER PTR_MSG_TARGET_CMD_BUFFER_POST_REPLY, + TargetCmdBufferPostReply_t, MPI_POINTER pTargetCmdBufferPostReply_t; + +/* the following structure is obsolete as of MPI v1.2 */ +typedef struct _MSG_PRIORITY_CMD_RECEIVED_REPLY +{ + U16 Reserved; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 PriorityReason; /* 0Ch */ + U8 Reserved3; /* 0Dh */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 ReplyWord; /* 14h */ +} MSG_PRIORITY_CMD_RECEIVED_REPLY, MPI_POINTER PTR_MSG_PRIORITY_CMD_RECEIVED_REPLY, + PriorityCommandReceivedReply_t, MPI_POINTER pPriorityCommandReceivedReply_t; + +#define PRIORITY_REASON_NO_DISCONNECT (0x00) +#define PRIORITY_REASON_SCSI_TASK_MANAGEMENT (0x01) +#define PRIORITY_REASON_CMD_PARITY_ERR (0x02) +#define PRIORITY_REASON_MSG_OUT_PARITY_ERR (0x03) +#define PRIORITY_REASON_LQ_CRC_ERR (0x04) +#define PRIORITY_REASON_CMD_CRC_ERR (0x05) +#define PRIORITY_REASON_PROTOCOL_ERR (0x06) +#define PRIORITY_REASON_DATA_OUT_PARITY_ERR (0x07) +#define PRIORITY_REASON_DATA_OUT_CRC_ERR (0x08) +#define PRIORITY_REASON_TARGET_BUSY (0x09) +#define PRIORITY_REASON_UNKNOWN (0xFF) + + +typedef struct _MSG_TARGET_CMD_BUFFER_POST_ERROR_REPLY +{ + U16 Reserved; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 PriorityReason; /* 0Ch */ + U8 Reserved3; /* 0Dh */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 ReplyWord; /* 14h */ +} MSG_TARGET_CMD_BUFFER_POST_ERROR_REPLY, + MPI_POINTER PTR_MSG_TARGET_CMD_BUFFER_POST_ERROR_REPLY, + TargetCmdBufferPostErrorReply_t, MPI_POINTER pTargetCmdBufferPostErrorReply_t; + + +typedef struct _MPI_TARGET_FCP_CMD_BUFFER +{ + U8 FcpLun[8]; /* 00h */ + U8 FcpCntl[4]; /* 08h */ + U8 FcpCdb[16]; /* 0Ch */ + U32 FcpDl; /* 1Ch */ + U8 AliasIndex; /* 20h */ + U8 Reserved1; /* 21h */ + U16 Reserved2; /* 22h */ +} MPI_TARGET_FCP_CMD_BUFFER, MPI_POINTER PTR_MPI_TARGET_FCP_CMD_BUFFER, + MpiTargetFcpCmdBuffer, MPI_POINTER pMpiTargetFcpCmdBuffer; + + +typedef struct _MPI_TARGET_SCSI_SPI_CMD_BUFFER +{ + /* SPI L_Q information unit */ + U8 L_QType; /* 00h */ + U8 Reserved; /* 01h */ + U16 Tag; /* 02h */ + U8 LogicalUnitNumber[8]; /* 04h */ + U32 DataLength; /* 0Ch */ + /* SPI command information unit */ + U8 ReservedFirstByteOfCommandIU; /* 10h */ + U8 TaskAttribute; /* 11h */ + U8 TaskManagementFlags; /* 12h */ + U8 AdditionalCDBLength; /* 13h */ + U8 CDB[16]; /* 14h */ +} MPI_TARGET_SCSI_SPI_CMD_BUFFER, + MPI_POINTER PTR_MPI_TARGET_SCSI_SPI_CMD_BUFFER, + MpiTargetScsiSpiCmdBuffer, MPI_POINTER pMpiTargetScsiSpiCmdBuffer; + + +/****************************************************************************/ +/* Target Assist Request */ +/****************************************************************************/ + +typedef struct _MSG_TARGET_ASSIST_REQUEST +{ + U8 StatusCode; /* 00h */ + U8 TargetAssistFlags; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 QueueTag; /* 04h */ + U8 Reserved; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 ReplyWord; /* 0Ch */ + U8 LUN[8]; /* 10h */ + U32 RelativeOffset; /* 18h */ + U32 DataLength; /* 1Ch */ + SGE_IO_UNION SGL[1]; /* 20h */ +} MSG_TARGET_ASSIST_REQUEST, MPI_POINTER PTR_MSG_TARGET_ASSIST_REQUEST, + TargetAssistRequest_t, MPI_POINTER pTargetAssistRequest_t; + +#define TARGET_ASSIST_FLAGS_DATA_DIRECTION (0x01) +#define TARGET_ASSIST_FLAGS_AUTO_STATUS (0x02) +#define TARGET_ASSIST_FLAGS_HIGH_PRIORITY (0x04) +#define TARGET_ASSIST_FLAGS_CONFIRMED (0x08) +#define TARGET_ASSIST_FLAGS_REPOST_CMD_BUFFER (0x80) + + +typedef struct _MSG_TARGET_ERROR_REPLY +{ + U16 Reserved; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U8 PriorityReason; /* 0Ch */ + U8 Reserved3; /* 0Dh */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 ReplyWord; /* 14h */ + U32 TransferCount; /* 18h */ +} MSG_TARGET_ERROR_REPLY, MPI_POINTER PTR_MSG_TARGET_ERROR_REPLY, + TargetErrorReply_t, MPI_POINTER pTargetErrorReply_t; + + +/****************************************************************************/ +/* Target Status Send Request */ +/****************************************************************************/ + +typedef struct _MSG_TARGET_STATUS_SEND_REQUEST +{ + U8 StatusCode; /* 00h */ + U8 StatusFlags; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 QueueTag; /* 04h */ + U8 Reserved; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 ReplyWord; /* 0Ch */ + U8 LUN[8]; /* 10h */ + SGE_SIMPLE_UNION StatusDataSGE; /* 18h */ +} MSG_TARGET_STATUS_SEND_REQUEST, MPI_POINTER PTR_MSG_TARGET_STATUS_SEND_REQUEST, + TargetStatusSendRequest_t, MPI_POINTER pTargetStatusSendRequest_t; + +#define TARGET_STATUS_SEND_FLAGS_AUTO_GOOD_STATUS (0x01) +#define TARGET_STATUS_SEND_FLAGS_HIGH_PRIORITY (0x04) +#define TARGET_STATUS_SEND_FLAGS_CONFIRMED (0x08) +#define TARGET_STATUS_SEND_FLAGS_REPOST_CMD_BUFFER (0x80) + +/* + * NOTE: FCP_RSP data is big-endian. When used on a little-endian system, this + * structure properly orders the bytes. + */ +typedef struct _MPI_TARGET_FCP_RSP_BUFFER +{ + U8 Reserved0[8]; /* 00h */ + U8 Reserved1[2]; /* 08h */ + U8 FcpFlags; /* 0Ah */ + U8 FcpStatus; /* 0Bh */ + U32 FcpResid; /* 0Ch */ + U32 FcpSenseLength; /* 10h */ + U32 FcpResponseLength; /* 14h */ + U8 FcpResponseData[8]; /* 18h */ + U8 FcpSenseData[32]; /* Pad to 64 bytes */ /* 20h */ +} MPI_TARGET_FCP_RSP_BUFFER, MPI_POINTER PTR_MPI_TARGET_FCP_RSP_BUFFER, + MpiTargetFcpRspBuffer, MPI_POINTER pMpiTargetFcpRspBuffer; + +/* + * NOTE: The SPI status IU is big-endian. When used on a little-endian system, + * this structure properly orders the bytes. + */ +typedef struct _MPI_TARGET_SCSI_SPI_STATUS_IU +{ + U8 Reserved0; /* 00h */ + U8 Reserved1; /* 01h */ + U8 Valid; /* 02h */ + U8 Status; /* 03h */ + U32 SenseDataListLength; /* 04h */ + U32 PktFailuresListLength; /* 08h */ + U8 SenseData[52]; /* Pad the IU to 64 bytes */ /* 0Ch */ +} MPI_TARGET_SCSI_SPI_STATUS_IU, MPI_POINTER PTR_MPI_TARGET_SCSI_SPI_STATUS_IU, + TargetScsiSpiStatusIU_t, MPI_POINTER pTargetScsiSpiStatusIU_t; + +/****************************************************************************/ +/* Target Mode Abort Request */ +/****************************************************************************/ + +typedef struct _MSG_TARGET_MODE_ABORT_REQUEST +{ + U8 AbortType; /* 00h */ + U8 Reserved; /* 01h */ + U8 ChainOffset; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U32 ReplyWord; /* 0Ch */ + U32 MsgContextToAbort; /* 10h */ +} MSG_TARGET_MODE_ABORT, MPI_POINTER PTR_MSG_TARGET_MODE_ABORT, + TargetModeAbort_t, MPI_POINTER pTargetModeAbort_t; + +#define TARGET_MODE_ABORT_TYPE_ALL_CMD_BUFFERS (0x00) +#define TARGET_MODE_ABORT_TYPE_ALL_IO (0x01) +#define TARGET_MODE_ABORT_TYPE_EXACT_IO (0x02) +#define TARGET_MODE_ABORT_TYPE_EXACT_IO_REQUEST (0x03) + +/* Target Mode Abort Reply */ + +typedef struct _MSG_TARGET_MODE_ABORT_REPLY +{ + U16 Reserved; /* 00h */ + U8 MsgLength; /* 02h */ + U8 Function; /* 03h */ + U16 Reserved1; /* 04h */ + U8 Reserved2; /* 06h */ + U8 MsgFlags; /* 07h */ + U32 MsgContext; /* 08h */ + U16 Reserved3; /* 0Ch */ + U16 IOCStatus; /* 0Eh */ + U32 IOCLogInfo; /* 10h */ + U32 AbortCount; /* 14h */ +} MSG_TARGET_MODE_ABORT_REPLY, MPI_POINTER PTR_MSG_TARGET_MODE_ABORT_REPLY, + TargetModeAbortReply_t, MPI_POINTER pTargetModeAbortReply_t; + + +/****************************************************************************/ +/* Target Mode Context Reply */ +/****************************************************************************/ + +#define TARGET_MODE_REPLY_IO_INDEX_MASK (0x00003FFF) +#define TARGET_MODE_REPLY_IO_INDEX_SHIFT (0) +#define TARGET_MODE_REPLY_INITIATOR_INDEX_MASK (0x03FFC000) +#define TARGET_MODE_REPLY_INITIATOR_INDEX_SHIFT (14) +#define TARGET_MODE_REPLY_ALIAS_MASK (0x04000000) +#define TARGET_MODE_REPLY_ALIAS_SHIFT (26) +#define TARGET_MODE_REPLY_PORT_MASK (0x10000000) +#define TARGET_MODE_REPLY_PORT_SHIFT (28) + + +#define GET_IO_INDEX(x) (((x) & TARGET_MODE_REPLY_IO_INDEX_MASK) \ + >> TARGET_MODE_REPLY_IO_INDEX_SHIFT) + +#define SET_IO_INDEX(t, i) \ + ((t) = ((t) & ~TARGET_MODE_REPLY_IO_INDEX_MASK) | \ + (((i) << TARGET_MODE_REPLY_IO_INDEX_SHIFT) & \ + TARGET_MODE_REPLY_IO_INDEX_MASK)) + +#define GET_INITIATOR_INDEX(x) (((x) & TARGET_MODE_REPLY_INITIATOR_INDEX_MASK) \ + >> TARGET_MODE_REPLY_INITIATOR_INDEX_SHIFT) + +#define SET_INITIATOR_INDEX(t, ii) \ + ((t) = ((t) & ~TARGET_MODE_REPLY_INITIATOR_INDEX_MASK) | \ + (((ii) << TARGET_MODE_REPLY_INITIATOR_INDEX_SHIFT) & \ + TARGET_MODE_REPLY_INITIATOR_INDEX_MASK)) + +#define GET_ALIAS(x) (((x) & TARGET_MODE_REPLY_ALIAS_MASK) \ + >> TARGET_MODE_REPLY_ALIAS_SHIFT) + +#define SET_ALIAS(t, a) ((t) = ((t) & ~TARGET_MODE_REPLY_ALIAS_MASK) | \ + (((a) << TARGET_MODE_REPLY_ALIAS_SHIFT) & \ + TARGET_MODE_REPLY_ALIAS_MASK)) + +#define GET_PORT(x) (((x) & TARGET_MODE_REPLY_PORT_MASK) \ + >> TARGET_MODE_REPLY_PORT_SHIFT) + +#define SET_PORT(t, p) ((t) = ((t) & ~TARGET_MODE_REPLY_PORT_MASK) | \ + (((p) << TARGET_MODE_REPLY_PORT_SHIFT) & \ + TARGET_MODE_REPLY_PORT_MASK)) + +/* the following obsolete values are for MPI v1.0 support */ +#define TARGET_MODE_REPLY_0100_MASK_HOST_INDEX (0x000003FF) +#define TARGET_MODE_REPLY_0100_SHIFT_HOST_INDEX (0) +#define TARGET_MODE_REPLY_0100_MASK_IOC_INDEX (0x001FF800) +#define TARGET_MODE_REPLY_0100_SHIFT_IOC_INDEX (11) +#define TARGET_MODE_REPLY_0100_PORT_MASK (0x00400000) +#define TARGET_MODE_REPLY_0100_PORT_SHIFT (22) +#define TARGET_MODE_REPLY_0100_MASK_INITIATOR_INDEX (0x1F800000) +#define TARGET_MODE_REPLY_0100_SHIFT_INITIATOR_INDEX (23) + +#define GET_HOST_INDEX_0100(x) (((x) & TARGET_MODE_REPLY_0100_MASK_HOST_INDEX) \ + >> TARGET_MODE_REPLY_0100_SHIFT_HOST_INDEX) + +#define SET_HOST_INDEX_0100(t, hi) \ + ((t) = ((t) & ~TARGET_MODE_REPLY_0100_MASK_HOST_INDEX) | \ + (((hi) << TARGET_MODE_REPLY_0100_SHIFT_HOST_INDEX) & \ + TARGET_MODE_REPLY_0100_MASK_HOST_INDEX)) + +#define GET_IOC_INDEX_0100(x) (((x) & TARGET_MODE_REPLY_0100_MASK_IOC_INDEX) \ + >> TARGET_MODE_REPLY_0100_SHIFT_IOC_INDEX) + +#define SET_IOC_INDEX_0100(t, ii) \ + ((t) = ((t) & ~TARGET_MODE_REPLY_0100_MASK_IOC_INDEX) | \ + (((ii) << TARGET_MODE_REPLY_0100_SHIFT_IOC_INDEX) & \ + TARGET_MODE_REPLY_0100_MASK_IOC_INDEX)) + +#define GET_INITIATOR_INDEX_0100(x) \ + (((x) & TARGET_MODE_REPLY_0100_MASK_INITIATOR_INDEX) \ + >> TARGET_MODE_REPLY_0100_SHIFT_INITIATOR_INDEX) + +#define SET_INITIATOR_INDEX_0100(t, ii) \ + ((t) = ((t) & ~TARGET_MODE_REPLY_0100_MASK_INITIATOR_INDEX) | \ + (((ii) << TARGET_MODE_REPLY_0100_SHIFT_INITIATOR_INDEX) & \ + TARGET_MODE_REPLY_0100_MASK_INITIATOR_INDEX)) + + +#endif + diff --git a/xen/drivers/message/fusion/lsi/mpi_type.h b/xen/drivers/message/fusion/lsi/mpi_type.h new file mode 100644 index 0000000000..7e94ee9d36 --- /dev/null +++ b/xen/drivers/message/fusion/lsi/mpi_type.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2000-2002 LSI Logic Corporation. + * + * + * Name: MPI_TYPE.H + * Title: MPI Basic type definitions + * Creation Date: June 6, 2000 + * + * MPI Version: 01.02.01 + * + * Version History + * --------------- + * + * Date Version Description + * -------- -------- ------------------------------------------------------ + * 05-08-00 00.10.01 Original release for 0.10 spec dated 4/26/2000. + * 06-06-00 01.00.01 Update version number for 1.0 release. + * 11-02-00 01.01.01 Original release for post 1.0 work + * 02-20-01 01.01.02 Added define and ifdef for MPI_POINTER. + * 08-08-01 01.02.01 Original release for v1.2 work. + * -------------------------------------------------------------------------- + */ + +#ifndef MPI_TYPE_H +#define MPI_TYPE_H + + +/******************************************************************************* + * Define MPI_POINTER if it hasn't already been defined. By default MPI_POINTER + * is defined to be a near pointer. MPI_POINTER can be defined as a far pointer + * by defining MPI_POINTER as "far *" before this header file is included. + */ +#ifndef MPI_POINTER +#define MPI_POINTER * +#endif + + +/***************************************************************************** +* +* B a s i c T y p e s +* +*****************************************************************************/ + +typedef signed char S8; +typedef unsigned char U8; +typedef signed short S16; +typedef unsigned short U16; + + +#if defined(unix) || defined(__arm) || defined(ALPHA) + + typedef signed int S32; + typedef unsigned int U32; + +#else + + typedef signed long S32; + typedef unsigned long U32; + +#endif + + +typedef struct _S64 +{ + U32 Low; + S32 High; +} S64; + +typedef struct _U64 +{ + U32 Low; + U32 High; +} U64; + + +/****************************************************************************/ +/* Pointers */ +/****************************************************************************/ + +typedef S8 *PS8; +typedef U8 *PU8; +typedef S16 *PS16; +typedef U16 *PU16; +typedef S32 *PS32; +typedef U32 *PU32; +typedef S64 *PS64; +typedef U64 *PU64; + + +#endif + diff --git a/xen/drivers/message/fusion/mptbase.c b/xen/drivers/message/fusion/mptbase.c new file mode 100644 index 0000000000..dcf6123114 --- /dev/null +++ b/xen/drivers/message/fusion/mptbase.c @@ -0,0 +1,6130 @@ +/* + * linux/drivers/message/fusion/mptbase.c + * High performance SCSI + LAN / Fibre Channel device drivers. + * This is the Fusion MPT base driver which supports multiple + * (SCSI + LAN) specialized protocol drivers. + * For use with PCI chip/adapter(s): + * LSIFC9xx/LSI409xx Fibre Channel + * running LSI Logic Fusion MPT (Message Passing Technology) firmware. + * + * Credits: + * There are lots of people not mentioned below that deserve credit + * and thanks but won't get it here - sorry in advance that you + * got overlooked. + * + * This driver would not exist if not for Alan Cox's development + * of the linux i2o driver. + * + * A special thanks to Noah Romer (LSI Logic) for tons of work + * and tough debugging on the LAN driver, especially early on;-) + * And to Roger Hickerson (LSI Logic) for tirelessly supporting + * this driver project. + * + * A special thanks to Pamela Delaney (LSI Logic) for tons of work + * and countless enhancements while adding support for the 1030 + * chip family. Pam has been instrumental in the development of + * of the 2.xx.xx series fusion drivers, and her contributions are + * far too numerous to hope to list in one place. + * + * All manner of help from Stephen Shirron (LSI Logic): + * low-level FC analysis, debug + various fixes in FCxx firmware, + * initial port to alpha platform, various driver code optimizations, + * being a faithful sounding board on all sorts of issues & ideas, + * etc. + * + * A huge debt of gratitude is owed to David S. Miller (DaveM) + * for fixing much of the stupid and broken stuff in the early + * driver while porting to sparc64 platform. THANK YOU! + * + * Special thanks goes to the I2O LAN driver people at the + * University of Helsinki, who, unbeknownst to them, provided + * the inspiration and initial structure for this driver. + * + * A really huge debt of gratitude is owed to Eddie C. Dost + * for gobs of hard work fixing and optimizing LAN code. + * THANK YOU! + * + * Copyright (c) 1999-2002 LSI Logic Corporation + * Originally By: Steven J. Ralston + * (mailto:sjralston1@netscape.net) + * (mailto:lstephens@lsil.com) + * + * $Id: mptbase.c,v 1.130 2003/05/07 14:08:30 pdelaney Exp $ + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* needed for in_interrupt() proto */ +#include /* SAE: Necessary... */ +#include +#ifdef CONFIG_MTRR +#include +#endif +#ifdef __sparc__ +#include /* needed for __irq_itoa() proto */ +#endif + +#include "mptbase.h" + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#define my_NAME "Fusion MPT base driver" +#define my_VERSION MPT_LINUX_VERSION_COMMON +#define MYNAM "mptbase" + +MODULE_AUTHOR(MODULEAUTHOR); +MODULE_DESCRIPTION(my_NAME); +MODULE_LICENSE("GPL"); + +/* + * cmd line parameters + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,59) +MODULE_PARM(PortIo, "0-1i"); +MODULE_PARM_DESC(PortIo, "[0]=Use mmap, 1=Use port io"); +#endif +static int PortIo = 0; + +#ifdef MFCNT +static int mfcounter = 0; +#define PRINT_MF_COUNT 20000 +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Public data... + */ +int mpt_lan_index = -1; +int mpt_stm_index = -1; + +/* SAE: Xen doesn't have proc */ +#if defined(CONFIG_PROC_FS) + +struct proc_dir_entry *mpt_proc_root_dir; + +#endif + +DmpServices_t *DmpService; + +void *mpt_v_ASCQ_TablePtr; +const char **mpt_ScsiOpcodesPtr; +int mpt_ASCQ_TableSz; + + +#define WHOINIT_UNKNOWN 0xAA + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Private data... + */ + /* Adapter lookup table */ + MPT_ADAPTER *mpt_adapters[MPT_MAX_ADAPTERS]; +static MPT_ADAPTER_TRACKER MptAdapters; + /* Callback lookup table */ +static MPT_CALLBACK MptCallbacks[MPT_MAX_PROTOCOL_DRIVERS]; + /* Protocol driver class lookup table */ +static int MptDriverClass[MPT_MAX_PROTOCOL_DRIVERS]; + /* Event handler lookup table */ +static MPT_EVHANDLER MptEvHandlers[MPT_MAX_PROTOCOL_DRIVERS]; + /* Reset handler lookup table */ +static MPT_RESETHANDLER MptResetHandlers[MPT_MAX_PROTOCOL_DRIVERS]; + +static int FusionInitCalled = 0; +static int mpt_base_index = -1; +static int last_drv_idx = -1; +static int isense_idx = -1; + +/* SAE: No wait queues or threads */ +#ifdef XENO_KILLED +static DECLARE_WAIT_QUEUE_HEAD(mpt_waitq); +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Forward protos... + */ +static void mpt_interrupt(int irq, void *bus_id, struct pt_regs *r); +static int mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply); + +static int mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag); +static int mpt_adapter_install(struct pci_dev *pdev); +static void mpt_detect_bound_ports(MPT_ADAPTER *this, struct pci_dev *pdev); +static void mpt_adapter_disable(MPT_ADAPTER *ioc, int freeup); +static void mpt_adapter_dispose(MPT_ADAPTER *ioc); + +static void MptDisplayIocCapabilities(MPT_ADAPTER *ioc); +static int MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag); +//static u32 mpt_GetIocState(MPT_ADAPTER *ioc, int cooked); +static int GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason); +static int GetPortFacts(MPT_ADAPTER *ioc, int portnum, int sleepFlag); +static int SendIocInit(MPT_ADAPTER *ioc, int sleepFlag); +static int SendPortEnable(MPT_ADAPTER *ioc, int portnum, int sleepFlag); +static int mpt_do_upload(MPT_ADAPTER *ioc, int sleepFlag); +static int mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag); +static int mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag); +static int KickStart(MPT_ADAPTER *ioc, int ignore, int sleepFlag); +static int SendIocReset(MPT_ADAPTER *ioc, u8 reset_type, int sleepFlag); +static int PrimeIocFifos(MPT_ADAPTER *ioc); +static int WaitForDoorbellAck(MPT_ADAPTER *ioc, int howlong, int sleepFlag); +static int WaitForDoorbellInt(MPT_ADAPTER *ioc, int howlong, int sleepFlag); +static int WaitForDoorbellReply(MPT_ADAPTER *ioc, int howlong, int sleepFlag); +static int GetLanConfigPages(MPT_ADAPTER *ioc); +static int GetFcPortPage0(MPT_ADAPTER *ioc, int portnum); +static int GetIoUnitPage2(MPT_ADAPTER *ioc); +static int mpt_GetScsiPortSettings(MPT_ADAPTER *ioc, int portnum); +static int mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum); +static int mpt_findImVolumes(MPT_ADAPTER *ioc); +static void mpt_read_ioc_pg_1(MPT_ADAPTER *ioc); +static void mpt_timer_expired(unsigned long data); +static int SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch); +static int SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp); + +#if defined(CONFIG_PROC_FS) +static int procmpt_create(void); +static int procmpt_destroy(void); +static int procmpt_summary_read(char *buf, char **start, off_t offset, + int request, int *eof, void *data); +static int procmpt_version_read(char *buf, char **start, off_t offset, + int request, int *eof, void *data); +static int procmpt_iocinfo_read(char *buf, char **start, off_t offset, + int request, int *eof, void *data); +#endif +static void mpt_get_fw_exp_ver(char *buf, MPT_ADAPTER *ioc); + +//int mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag); +static int ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *evReply, int *evHandlers); +static void mpt_fc_log_info(MPT_ADAPTER *ioc, u32 log_info); +static void mpt_sp_log_info(MPT_ADAPTER *ioc, u32 log_info); + +int fusion_init(void); +static void fusion_exit(void); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * more Private data... + */ +#if defined(CONFIG_PROC_FS) +struct _mpt_proc_list { + const char *name; + int (*f)(char *, char **, off_t, int, int *, void *); +} mpt_proc_list[] = { + { "summary", procmpt_summary_read}, + { "version", procmpt_version_read}, +}; +#define MPT_PROC_ENTRIES (sizeof(mpt_proc_list)/sizeof(mpt_proc_list[0])) + +struct _mpt_ioc_proc_list { + const char *name; + int (*f)(char *, char **, off_t, int, int *, void *); +} mpt_ioc_proc_list[] = { + { "info", procmpt_iocinfo_read}, + { "summary", procmpt_summary_read}, +}; +#define MPT_IOC_PROC_ENTRIES (sizeof(mpt_ioc_proc_list)/sizeof(mpt_ioc_proc_list[0])) + +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* 20000207 -sralston + * GRRRRR... IOSpace (port i/o) register access (for the 909) is back! + * 20000517 -sralston + * Let's trying going back to default mmap register access... + */ + +static inline u32 CHIPREG_READ32(volatile u32 *a) +{ + if (PortIo) + return inl((unsigned long)a); + else + return readl(a); +} + +static inline void CHIPREG_WRITE32(volatile u32 *a, u32 v) +{ + if (PortIo) + outl(v, (unsigned long)a); + else + writel(v, a); +} + +static inline void CHIPREG_PIO_WRITE32(volatile u32 *a, u32 v) +{ + outl(v, (unsigned long)a); +} + +static inline u32 CHIPREG_PIO_READ32(volatile u32 *a) +{ + return inl((unsigned long)a); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_interrupt - MPT adapter (IOC) specific interrupt handler. + * @irq: irq number (not used) + * @bus_id: bus identifier cookie == pointer to MPT_ADAPTER structure + * @r: pt_regs pointer (not used) + * + * This routine is registered via the request_irq() kernel API call, + * and handles all interrupts generated from a specific MPT adapter + * (also referred to as a IO Controller or IOC). + * This routine must clear the interrupt from the adapter and does + * so by reading the reply FIFO. Multiple replies may be processed + * per single call to this routine; up to MPT_MAX_REPLIES_PER_ISR + * which is currently set to 32 in mptbase.h. + * + * This routine handles register-level access of the adapter but + * dispatches (calls) a protocol-specific callback routine to handle + * the protocol-specific details of the MPT request completion. + */ +static void +mpt_interrupt(int irq, void *bus_id, struct pt_regs *r) +{ + MPT_ADAPTER *ioc; + MPT_FRAME_HDR *mf; + MPT_FRAME_HDR *mr; + u32 pa; + int req_idx = -1; + int cb_idx; + int type; + int freeme; + int count = 0; + + ioc = bus_id; + + /* + * Verify ioc pointer is ok + */ + { + MPT_ADAPTER *iocCmp; + iocCmp = mpt_adapter_find_first(); + while ((ioc != iocCmp) && iocCmp) + iocCmp = mpt_adapter_find_next(iocCmp); + + if (!iocCmp) { + printk(KERN_WARNING "mpt_interrupt: Invalid ioc!\n"); + return; + } + } + + /* + * Drain the reply FIFO! + * + * NOTES: I've seen up to 10 replies processed in this loop, so far... + * Update: I've seen up to 9182 replies processed in this loop! ?? + * Update: Limit ourselves to processing max of N replies + * (bottom of loop). + */ + while (1) { + + if ((pa = CHIPREG_READ32(&ioc->chip->ReplyFifo)) == 0xFFFFFFFF) + return; + + cb_idx = 0; + freeme = 0; + + /* + * Check for non-TURBO reply! + */ + if (pa & MPI_ADDRESS_REPLY_A_BIT) { + u32 reply_dma_low; + u16 ioc_stat; + + /* non-TURBO reply! Hmmm, something may be up... + * Newest turbo reply mechanism; get address + * via left shift 1 (get rid of MPI_ADDRESS_REPLY_A_BIT)! + */ + + /* Map DMA address of reply header to cpu address. + * pa is 32 bits - but the dma address may be 32 or 64 bits + * get offset based only only the low addresses + */ + reply_dma_low = (pa = (pa << 1)); + mr = (MPT_FRAME_HDR *)((u8 *)ioc->reply_frames + + (reply_dma_low - ioc->reply_frames_low_dma)); + + req_idx = le16_to_cpu(mr->u.frame.hwhdr.msgctxu.fld.req_idx); + cb_idx = mr->u.frame.hwhdr.msgctxu.fld.cb_idx; + mf = MPT_INDEX_2_MFPTR(ioc, req_idx); + + dprintk((MYIOC_s_INFO_FMT "Got non-TURBO reply=%p\n", + ioc->name, mr)); + DBG_DUMP_REPLY_FRAME(mr) + + /* NEW! 20010301 -sralston + * Check/log IOC log info + */ + ioc_stat = le16_to_cpu(mr->u.reply.IOCStatus); + if (ioc_stat & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) { + u32 log_info = le32_to_cpu(mr->u.reply.IOCLogInfo); + if ((int)ioc->chip_type <= (int)FC929) + mpt_fc_log_info(ioc, log_info); + else + mpt_sp_log_info(ioc, log_info); + } + } else { + /* + * Process turbo (context) reply... + */ + dirqprintk((MYIOC_s_INFO_FMT "Got TURBO reply(=%08x)\n", ioc->name, pa)); + type = (pa >> MPI_CONTEXT_REPLY_TYPE_SHIFT); + if (type == MPI_CONTEXT_REPLY_TYPE_SCSI_TARGET) { + cb_idx = mpt_stm_index; + mf = NULL; + mr = (MPT_FRAME_HDR *) CAST_U32_TO_PTR(pa); + } else if (type == MPI_CONTEXT_REPLY_TYPE_LAN) { + cb_idx = mpt_lan_index; + /* + * BUG FIX! 20001218 -sralston + * Blind set of mf to NULL here was fatal + * after lan_reply says "freeme" + * Fix sort of combined with an optimization here; + * added explicit check for case where lan_reply + * was just returning 1 and doing nothing else. + * For this case skip the callback, but set up + * proper mf value first here:-) + */ + if ((pa & 0x58000000) == 0x58000000) { + req_idx = pa & 0x0000FFFF; + mf = MPT_INDEX_2_MFPTR(ioc, req_idx); + freeme = 1; + /* + * IMPORTANT! Invalidate the callback! + */ + cb_idx = 0; + } else { + mf = NULL; + } + mr = (MPT_FRAME_HDR *) CAST_U32_TO_PTR(pa); + } else { + req_idx = pa & 0x0000FFFF; + cb_idx = (pa & 0x00FF0000) >> 16; + mf = MPT_INDEX_2_MFPTR(ioc, req_idx); + mr = NULL; + } + pa = 0; /* No reply flush! */ + } + +#ifdef MPT_DEBUG_IRQ + if ((int)ioc->chip_type > (int)FC929) { + /* Verify mf, mr are reasonable. + */ + if ((mf) && ((mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth)) + || (mf < ioc->req_frames)) ) { + printk(MYIOC_s_WARN_FMT + "mpt_interrupt: Invalid mf (%p) req_idx (%d)!\n", ioc->name, (void *)mf, req_idx); + cb_idx = 0; + pa = 0; + freeme = 0; + } + if ((pa) && (mr) && ((mr >= MPT_INDEX_2_RFPTR(ioc, ioc->req_depth)) + || (mr < ioc->reply_frames)) ) { + printk(MYIOC_s_WARN_FMT + "mpt_interrupt: Invalid rf (%p)!\n", ioc->name, (void *)mr); + cb_idx = 0; + pa = 0; + freeme = 0; + } + if (cb_idx > (MPT_MAX_PROTOCOL_DRIVERS-1)) { + printk(MYIOC_s_WARN_FMT + "mpt_interrupt: Invalid cb_idx (%d)!\n", ioc->name, cb_idx); + cb_idx = 0; + pa = 0; + freeme = 0; + } + } +#endif + + /* Check for (valid) IO callback! */ + if (cb_idx) { + /* Do the callback! */ + freeme = (*(MptCallbacks[cb_idx]))(ioc, mf, mr); + } + + if (pa) { + /* Flush (non-TURBO) reply with a WRITE! */ + CHIPREG_WRITE32(&ioc->chip->ReplyFifo, pa); + } + + if (freeme) { + unsigned long flags; + + /* Put Request back on FreeQ! */ + spin_lock_irqsave(&ioc->FreeQlock, flags); + Q_ADD_TAIL(&ioc->FreeQ, &mf->u.frame.linkage, MPT_FRAME_HDR); +#ifdef MFCNT + ioc->mfcnt--; +#endif + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + } + + count++; + dirqprintk((MYIOC_s_INFO_FMT "ISR processed frame #%d\n", ioc->name, count)); + mb(); + + if (count >= MPT_MAX_REPLIES_PER_ISR) { + dirqprintk((MYIOC_s_INFO_FMT "ISR processed %d replies.", + ioc->name, count)); + dirqprintk((" Giving this ISR a break!\n")); + return; + } + + } /* drain reply FIFO */ +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_base_reply - MPT base driver's callback routine; all base driver + * "internal" request/reply processing is routed here. + * Currently used for EventNotification and EventAck handling. + * @ioc: Pointer to MPT_ADAPTER structure + * @mf: Pointer to original MPT request frame + * @reply: Pointer to MPT reply frame (NULL if TurboReply) + * + * Returns 1 indicating original alloc'd request frame ptr + * should be freed, or 0 if it shouldn't. + */ +static int +mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *reply) +{ + int freereq = 1; + u8 func; + + dprintk((MYIOC_s_INFO_FMT "mpt_base_reply() called\n", ioc->name)); + + if ((mf == NULL) || + (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) { + printk(MYIOC_s_ERR_FMT "NULL or BAD request frame ptr! (=%p)\n", + ioc->name, (void *)mf); + return 1; + } + + if (reply == NULL) { + dprintk((MYIOC_s_ERR_FMT "Unexpected NULL Event (turbo?) reply!\n", + ioc->name)); + return 1; + } + + if (!(reply->u.hdr.MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY)) { + dmfprintk((KERN_INFO MYNAM ": Original request frame (@%p) header\n", mf)); + DBG_DUMP_REQUEST_FRAME_HDR(mf) + } + + func = reply->u.hdr.Function; + dprintk((MYIOC_s_INFO_FMT "mpt_base_reply, Function=%02Xh\n", + ioc->name, func)); + + if (func == MPI_FUNCTION_EVENT_NOTIFICATION) { + EventNotificationReply_t *pEvReply = (EventNotificationReply_t *) reply; + int evHandlers = 0; + int results; + + results = ProcessEventNotification(ioc, pEvReply, &evHandlers); + if (results != evHandlers) { + /* CHECKME! Any special handling needed here? */ + dprintk((MYIOC_s_WARN_FMT "Called %d event handlers, sum results = %d\n", + ioc->name, evHandlers, results)); + } + + /* + * Hmmm... It seems that EventNotificationReply is an exception + * to the rule of one reply per request. + */ + if (pEvReply->MsgFlags & MPI_MSGFLAGS_CONTINUATION_REPLY) + freereq = 0; + +#ifdef CONFIG_PROC_FS +// LogEvent(ioc, pEvReply); +#endif + + } else if (func == MPI_FUNCTION_EVENT_ACK) { + dprintk((MYIOC_s_INFO_FMT "mpt_base_reply, EventAck reply received\n", + ioc->name)); + } else if (func == MPI_FUNCTION_CONFIG) { + CONFIGPARMS *pCfg; + unsigned long flags; + + dcprintk((MYIOC_s_INFO_FMT "config_complete (mf=%p,mr=%p)\n", + ioc->name, mf, reply)); + + pCfg = * ((CONFIGPARMS **)((u8 *) mf + ioc->req_sz - sizeof(void *))); + + if (pCfg) { + /* disable timer and remove from linked list */ + del_timer(&pCfg->timer); + + spin_lock_irqsave(&ioc->FreeQlock, flags); + Q_DEL_ITEM(&pCfg->linkage); + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + + /* + * If IOC Status is SUCCESS, save the header + * and set the status code to GOOD. + */ + pCfg->status = MPT_CONFIG_ERROR; + if (reply) { + ConfigReply_t *pReply = (ConfigReply_t *)reply; + u16 status; + + status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK; + dcprintk((KERN_NOTICE " IOCStatus=%04xh, IOCLogInfo=%08xh\n", + status, le32_to_cpu(pReply->IOCLogInfo))); + + pCfg->status = status; + if (status == MPI_IOCSTATUS_SUCCESS) { + pCfg->hdr->PageVersion = pReply->Header.PageVersion; + pCfg->hdr->PageLength = pReply->Header.PageLength; + pCfg->hdr->PageNumber = pReply->Header.PageNumber; + pCfg->hdr->PageType = pReply->Header.PageType; + } + } + + /* + * Wake up the original calling thread + */ + pCfg->wait_done = 1; +#ifdef XENO_KILLED + wake_up(&mpt_waitq); +#else + mdelay(250); +#endif + } + } else { + printk(MYIOC_s_ERR_FMT "Unexpected msg function (=%02Xh) reply received!\n", + ioc->name, func); + } + + /* + * Conditionally tell caller to free the original + * EventNotification/EventAck/unexpected request frame! + */ + return freereq; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_register - Register protocol-specific main callback handler. + * @cbfunc: callback function pointer + * @dclass: Protocol driver's class (%MPT_DRIVER_CLASS enum value) + * + * This routine is called by a protocol-specific driver (SCSI host, + * LAN, SCSI target) to register it's reply callback routine. Each + * protocol-specific driver must do this before it will be able to + * use any IOC resources, such as obtaining request frames. + * + * NOTES: The SCSI protocol driver currently calls this routine thrice + * in order to register separate callbacks; one for "normal" SCSI IO; + * one for MptScsiTaskMgmt requests; one for Scan/DV requests. + * + * Returns a positive integer valued "handle" in the + * range (and S.O.D. order) {N,...,7,6,5,...,1} if successful. + * Any non-positive return value (including zero!) should be considered + * an error by the caller. + */ +int +mpt_register(MPT_CALLBACK cbfunc, MPT_DRIVER_CLASS dclass) +{ + int i; + + last_drv_idx = -1; + +#ifndef MODULE + /* + * Handle possibility of the mptscsih_detect() routine getting + * called *before* fusion_init! + */ + if (!FusionInitCalled) { + dprintk((KERN_INFO MYNAM ": Hmmm, calling fusion_init from mpt_register!\n")); + /* + * NOTE! We'll get recursion here, as fusion_init() + * calls mpt_register()! + */ + fusion_init(); + FusionInitCalled++; + } +#endif + + /* + * Search for empty callback slot in this order: {N,...,7,6,5,...,1} + * (slot/handle 0 is reserved!) + */ + for (i = MPT_MAX_PROTOCOL_DRIVERS-1; i; i--) { + if (MptCallbacks[i] == NULL) { + MptCallbacks[i] = cbfunc; + MptDriverClass[i] = dclass; + MptEvHandlers[i] = NULL; + last_drv_idx = i; + if (cbfunc != mpt_base_reply) { + mpt_inc_use_count(); + } + break; + } + } + + return last_drv_idx; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_deregister - Deregister a protocol drivers resources. + * @cb_idx: previously registered callback handle + * + * Each protocol-specific driver should call this routine when it's + * module is unloaded. + */ +void +mpt_deregister(int cb_idx) +{ + if ((cb_idx >= 0) && (cb_idx < MPT_MAX_PROTOCOL_DRIVERS)) { + MptCallbacks[cb_idx] = NULL; + MptDriverClass[cb_idx] = MPTUNKNOWN_DRIVER; + MptEvHandlers[cb_idx] = NULL; + + last_drv_idx++; + if (isense_idx != -1 && isense_idx <= cb_idx) + isense_idx++; + + if (cb_idx != mpt_base_index) { + mpt_dec_use_count(); + } + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_event_register - Register protocol-specific event callback + * handler. + * @cb_idx: previously registered (via mpt_register) callback handle + * @ev_cbfunc: callback function + * + * This routine can be called by one or more protocol-specific drivers + * if/when they choose to be notified of MPT events. + * + * Returns 0 for success. + */ +int +mpt_event_register(int cb_idx, MPT_EVHANDLER ev_cbfunc) +{ + if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS) + return -1; + + MptEvHandlers[cb_idx] = ev_cbfunc; + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_event_deregister - Deregister protocol-specific event callback + * handler. + * @cb_idx: previously registered callback handle + * + * Each protocol-specific driver should call this routine + * when it does not (or can no longer) handle events, + * or when it's module is unloaded. + */ +void +mpt_event_deregister(int cb_idx) +{ + if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS) + return; + + MptEvHandlers[cb_idx] = NULL; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_reset_register - Register protocol-specific IOC reset handler. + * @cb_idx: previously registered (via mpt_register) callback handle + * @reset_func: reset function + * + * This routine can be called by one or more protocol-specific drivers + * if/when they choose to be notified of IOC resets. + * + * Returns 0 for success. + */ +int +mpt_reset_register(int cb_idx, MPT_RESETHANDLER reset_func) +{ + if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS) + return -1; + + MptResetHandlers[cb_idx] = reset_func; + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_reset_deregister - Deregister protocol-specific IOC reset handler. + * @cb_idx: previously registered callback handle + * + * Each protocol-specific driver should call this routine + * when it does not (or can no longer) handle IOC reset handling, + * or when it's module is unloaded. + */ +void +mpt_reset_deregister(int cb_idx) +{ + if (cb_idx < 1 || cb_idx >= MPT_MAX_PROTOCOL_DRIVERS) + return; + + MptResetHandlers[cb_idx] = NULL; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_get_msg_frame - Obtain a MPT request frame from the pool (of 1024) + * allocated per MPT adapter. + * @handle: Handle of registered MPT protocol driver + * @iocid: IOC unique identifier (integer) + * + * Returns pointer to a MPT request frame or %NULL if none are available + * or IOC is not active. + */ +MPT_FRAME_HDR* +mpt_get_msg_frame(int handle, int iocid) +{ + MPT_FRAME_HDR *mf = NULL; + MPT_ADAPTER *iocp; + unsigned long flags; + + /* validate handle and ioc identifier */ + iocp = mpt_adapters[iocid]; + +#ifdef MFCNT + if (!iocp->active) + printk(KERN_WARNING "IOC Not Active! mpt_get_msg_frame returning NULL!\n"); +#endif + + /* If interrupts are not attached, do not return a request frame */ + if (!iocp->active) + return NULL; + + spin_lock_irqsave(&iocp->FreeQlock, flags); + if (! Q_IS_EMPTY(&iocp->FreeQ)) { + int req_offset; + + mf = iocp->FreeQ.head; + Q_DEL_ITEM(&mf->u.frame.linkage); + mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; /* byte */ + req_offset = (u8 *)mf - (u8 *)iocp->req_frames; + /* u16! */ + mf->u.frame.hwhdr.msgctxu.fld.req_idx = + cpu_to_le16(req_offset / iocp->req_sz); + mf->u.frame.hwhdr.msgctxu.fld.rsvd = 0; +#ifdef MFCNT + iocp->mfcnt++; +#endif + } + spin_unlock_irqrestore(&iocp->FreeQlock, flags); + +#ifdef MFCNT + if (mf == NULL) + printk(KERN_WARNING "IOC Active. No free Msg Frames! Count 0x%x Max 0x%x\n", iocp->mfcnt, iocp->req_depth); + mfcounter++; + if (mfcounter == PRINT_MF_COUNT) + printk(KERN_INFO "MF Count 0x%x Max 0x%x \n", iocp->mfcnt, iocp->req_depth); +#endif + + return mf; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_put_msg_frame - Send a protocol specific MPT request frame + * to a IOC. + * @handle: Handle of registered MPT protocol driver + * @iocid: IOC unique identifier (integer) + * @mf: Pointer to MPT request frame + * + * This routine posts a MPT request frame to the request post FIFO of a + * specific MPT adapter. + */ +void +mpt_put_msg_frame(int handle, int iocid, MPT_FRAME_HDR *mf) +{ + MPT_ADAPTER *iocp; + + iocp = mpt_adapters[iocid]; + if (iocp != NULL) { + u32 mf_dma_addr; + int req_offset; + + /* ensure values are reset properly! */ + mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; /* byte */ + req_offset = (u8 *)mf - (u8 *)iocp->req_frames; + /* u16! */ + mf->u.frame.hwhdr.msgctxu.fld.req_idx = cpu_to_le16(req_offset / iocp->req_sz); + mf->u.frame.hwhdr.msgctxu.fld.rsvd = 0; + +#ifdef MPT_DEBUG_MSG_FRAME + { + u32 *m = mf->u.frame.hwhdr.__hdr; + int ii, n; + + printk(KERN_INFO MYNAM ": %s: About to Put msg frame @ %p:\n" KERN_INFO " ", + iocp->name, m); + n = iocp->req_sz/4 - 1; + while (m[n] == 0) + n--; + for (ii=0; ii<=n; ii++) { + if (ii && ((ii%8)==0)) + printk("\n" KERN_INFO " "); + printk(" %08x", le32_to_cpu(m[ii])); + } + printk("\n"); + } +#endif + + mf_dma_addr = iocp->req_frames_low_dma + req_offset; + CHIPREG_WRITE32(&iocp->chip->RequestFifo, mf_dma_addr); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_free_msg_frame - Place MPT request frame back on FreeQ. + * @handle: Handle of registered MPT protocol driver + * @iocid: IOC unique identifier (integer) + * @mf: Pointer to MPT request frame + * + * This routine places a MPT request frame back on the MPT adapter's + * FreeQ. + */ +void +mpt_free_msg_frame(int handle, int iocid, MPT_FRAME_HDR *mf) +{ + MPT_ADAPTER *iocp; + unsigned long flags; + + iocp = mpt_adapters[iocid]; + if (iocp != NULL) { + /* Put Request back on FreeQ! */ + spin_lock_irqsave(&iocp->FreeQlock, flags); + Q_ADD_TAIL(&iocp->FreeQ, &mf->u.frame.linkage, MPT_FRAME_HDR); +#ifdef MFCNT + iocp->mfcnt--; +#endif + spin_unlock_irqrestore(&iocp->FreeQlock, flags); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_add_sge - Place a simple SGE at address pAddr. + * @pAddr: virtual address for SGE + * @flagslength: SGE flags and data transfer length + * @dma_addr: Physical address + * + * This routine places a MPT request frame back on the MPT adapter's + * FreeQ. + */ +void +mpt_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr) +{ + if (sizeof(dma_addr_t) == sizeof(u64)) { + SGESimple64_t *pSge = (SGESimple64_t *) pAddr; + u32 tmp = dma_addr & 0xFFFFFFFF; + + pSge->FlagsLength = cpu_to_le32(flagslength); + pSge->Address.Low = cpu_to_le32(tmp); + tmp = (u32) ((u64)dma_addr >> 32); + pSge->Address.High = cpu_to_le32(tmp); + + } else { + SGESimple32_t *pSge = (SGESimple32_t *) pAddr; + pSge->FlagsLength = cpu_to_le32(flagslength); + pSge->Address = cpu_to_le32(dma_addr); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_add_chain - Place a chain SGE at address pAddr. + * @pAddr: virtual address for SGE + * @next: nextChainOffset value (u32's) + * @length: length of next SGL segment + * @dma_addr: Physical address + * + * This routine places a MPT request frame back on the MPT adapter's + * FreeQ. + */ +void +mpt_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr) +{ + if (sizeof(dma_addr_t) == sizeof(u64)) { + SGEChain64_t *pChain = (SGEChain64_t *) pAddr; + u32 tmp = dma_addr & 0xFFFFFFFF; + + pChain->Length = cpu_to_le16(length); + pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size(); + + pChain->NextChainOffset = next; + + pChain->Address.Low = cpu_to_le32(tmp); + tmp = (u32) ((u64)dma_addr >> 32); + pChain->Address.High = cpu_to_le32(tmp); + } else { + SGEChain32_t *pChain = (SGEChain32_t *) pAddr; + pChain->Length = cpu_to_le16(length); + pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size(); + pChain->NextChainOffset = next; + pChain->Address = cpu_to_le32(dma_addr); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_send_handshake_request - Send MPT request via doorbell + * handshake method. + * @handle: Handle of registered MPT protocol driver + * @iocid: IOC unique identifier (integer) + * @reqBytes: Size of the request in bytes + * @req: Pointer to MPT request frame + * @sleepFlag: Use schedule if CAN_SLEEP else use udelay. + * + * This routine is used exclusively to send MptScsiTaskMgmt + * requests since they are required to be sent via doorbell handshake. + * + * NOTE: It is the callers responsibility to byte-swap fields in the + * request which are greater than 1 byte in size. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt_send_handshake_request(int handle, int iocid, int reqBytes, u32 *req, int sleepFlag) +{ + MPT_ADAPTER *iocp; + int r = 0; + + iocp = mpt_adapters[iocid]; + if (iocp != NULL) { + u8 *req_as_bytes; + int ii; + + /* State is known to be good upon entering + * this function so issue the bus reset + * request. + */ + + /* + * Emulate what mpt_put_msg_frame() does /wrt to sanity + * setting cb_idx/req_idx. But ONLY if this request + * is in proper (pre-alloc'd) request buffer range... + */ + ii = MFPTR_2_MPT_INDEX(iocp,(MPT_FRAME_HDR*)req); + if (reqBytes >= 12 && ii >= 0 && ii < iocp->req_depth) { + MPT_FRAME_HDR *mf = (MPT_FRAME_HDR*)req; + mf->u.frame.hwhdr.msgctxu.fld.req_idx = cpu_to_le16(ii); + mf->u.frame.hwhdr.msgctxu.fld.cb_idx = handle; + } + + /* Make sure there are no doorbells */ + CHIPREG_WRITE32(&iocp->chip->IntStatus, 0); + + CHIPREG_WRITE32(&iocp->chip->Doorbell, + ((MPI_FUNCTION_HANDSHAKE<chip->Doorbell) & MPI_DOORBELL_ACTIVE)) + return -5; + + dhsprintk((KERN_INFO MYNAM ": %s: mpt_send_handshake_request start, WaitCnt=%d\n", + iocp->name, ii)); + + CHIPREG_WRITE32(&iocp->chip->IntStatus, 0); + + if ((r = WaitForDoorbellAck(iocp, 2, sleepFlag)) < 0) { + return -2; + } + + /* Send request via doorbell handshake */ + req_as_bytes = (u8 *) req; + for (ii = 0; ii < reqBytes/4; ii++) { + u32 word; + + word = ((req_as_bytes[(ii*4) + 0] << 0) | + (req_as_bytes[(ii*4) + 1] << 8) | + (req_as_bytes[(ii*4) + 2] << 16) | + (req_as_bytes[(ii*4) + 3] << 24)); + CHIPREG_WRITE32(&iocp->chip->Doorbell, word); + if ((r = WaitForDoorbellAck(iocp, 2, sleepFlag)) < 0) { + r = -3; + break; + } + } + + if (r >= 0 && WaitForDoorbellInt(iocp, 10, sleepFlag) >= 0) + r = 0; + else + r = -4; + + /* Make sure there are no doorbells */ + CHIPREG_WRITE32(&iocp->chip->IntStatus, 0); + } + + return r; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_adapter_find_first - Find first MPT adapter pointer. + * + * Returns first MPT adapter pointer or %NULL if no MPT adapters + * are present. + */ +MPT_ADAPTER * +mpt_adapter_find_first(void) +{ + MPT_ADAPTER *this = NULL; + + if (! Q_IS_EMPTY(&MptAdapters)) + this = MptAdapters.head; + + return this; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_adapter_find_next - Find next MPT adapter pointer. + * @prev: Pointer to previous MPT adapter + * + * Returns next MPT adapter pointer or %NULL if there are no more. + */ +MPT_ADAPTER * +mpt_adapter_find_next(MPT_ADAPTER *prev) +{ + MPT_ADAPTER *next = NULL; + + if (prev && (prev->forw != (MPT_ADAPTER*)&MptAdapters.head)) + next = prev->forw; + + return next; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_pci_scan - Scan PCI devices for MPT adapters. + * + * Returns count of MPT adapters found, keying off of PCI vendor and + * device_id's. + */ +static int __init +mpt_pci_scan(void) +{ + struct pci_dev *pdev; + struct pci_dev *pdev2; + int found = 0; + int count = 0; + int r; + + dprintk((KERN_INFO MYNAM ": Checking for MPT adapters...\n")); + + /* + * NOTE: The 929, 929X, 1030 and 1035 will appear as 2 separate PCI devices, + * one for each channel. + */ + pci_for_each_dev(pdev) { + pdev2 = NULL; + if (pdev->vendor != 0x1000) + continue; + + if ((pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC909) && + (pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC929) && + (pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC919) && + (pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC929X) && + (pdev->device != MPI_MANUFACTPAGE_DEVICEID_FC919X) && + (pdev->device != MPI_MANUFACTPAGE_DEVID_53C1030) && + (pdev->device != MPI_MANUFACTPAGE_DEVID_1030_53C1035) && + 1) { + dprintk((KERN_INFO MYNAM ": Skipping LSI device=%04xh\n", pdev->device)); + continue; + } + + /* GRRRRR + * dual function devices (929, 929X, 1030, 1035) may be presented in Func 1,0 order, + * but we'd really really rather have them in Func 0,1 order. + * Do some kind of look ahead here... + */ + if (pdev->devfn & 1) { + /* SAE: Not a clear macro */ + pdev2 = NULL; + if(pdev != pci_dev_g(&pci_devices)) + pdev2 = pci_dev_g(pdev->global_list.next); + + if (pdev2 && (pdev2->vendor == 0x1000) && + (PCI_SLOT(pdev2->devfn) == PCI_SLOT(pdev->devfn)) && + (pdev2->device == pdev->device) && + (pdev2->bus->number == pdev->bus->number) && + !(pdev2->devfn & 1)) { + dprintk((KERN_INFO MYNAM ": MPT adapter found: PCI bus/dfn=%02x/%02xh, class=%08x, id=%xh\n", + pdev2->bus->number, pdev2->devfn, pdev2->class, pdev2->device)); + found++; + if ((r = mpt_adapter_install(pdev2)) == 0) + count++; + } else { + pdev2 = NULL; + } + } + + dprintk((KERN_INFO MYNAM ": MPT adapter found: PCI bus/dfn=%02x/%02xh, class=%08x, id=%xh\n", + pdev->bus->number, pdev->devfn, pdev->class, pdev->device)); + found++; + if ((r = mpt_adapter_install(pdev)) == 0) + count++; + + if (pdev2) + pdev = pdev2; + } + + if (!found || !count) { + fusion_exit(); + return -ENODEV; + } + + printk(KERN_INFO MYNAM ": %d MPT adapter%s found, %d installed.\n", + found, (found==1) ? "" : "s", count); + +#ifdef CONFIG_PROC_FS + (void) procmpt_create(); +#endif + + return count; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_verify_adapter - Given a unique IOC identifier, set pointer to + * the associated MPT adapter structure. + * @iocid: IOC unique identifier (integer) + * @iocpp: Pointer to pointer to IOC adapter + * + * Returns iocid and sets iocpp. + */ +int +mpt_verify_adapter(int iocid, MPT_ADAPTER **iocpp) +{ + MPT_ADAPTER *p; + + *iocpp = NULL; + if (iocid >= MPT_MAX_ADAPTERS) + return -1; + + p = mpt_adapters[iocid]; + if (p == NULL) + return -1; + + *iocpp = p; + return iocid; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_adapter_install - Install a PCI intelligent MPT adapter. + * @pdev: Pointer to pci_dev structure + * + * This routine performs all the steps necessary to bring the IOC of + * a MPT adapter to a OPERATIONAL state. This includes registering + * memory regions, registering the interrupt, and allocating request + * and reply memory pools. + * + * This routine also pre-fetches the LAN MAC address of a Fibre Channel + * MPT adapter. + * + * Returns 0 for success, non-zero for failure. + * + * TODO: Add support for polled controllers + */ +static int __init +mpt_adapter_install(struct pci_dev *pdev) +{ + MPT_ADAPTER *ioc; + u8 *mem; + unsigned long mem_phys; + unsigned long port; + u32 msize; + u32 psize; + int ii; + int r = -ENODEV; + u64 mask = 0xffffffffffffffffULL; + + if (pci_enable_device(pdev)) + return r; + + if (!pci_set_dma_mask(pdev, mask)) { + dprintk((KERN_INFO MYNAM ": 64 BIT PCI BUS DMA ADDRESSING SUPPORTED\n")); + } else { + if (pci_set_dma_mask(pdev, (u64) 0xffffffff)) { + printk(KERN_WARNING MYNAM + ": 32 BIT PCI BUS DMA ADDRESSING NOT SUPPORTED\n"); + return r; + } + } + + ioc = kmalloc(sizeof(MPT_ADAPTER), GFP_ATOMIC); + if (ioc == NULL) { + printk(KERN_ERR MYNAM ": ERROR - Insufficient memory to add adapter!\n"); + return -ENOMEM; + } + memset(ioc, 0, sizeof(*ioc)); + ioc->alloc_total = sizeof(MPT_ADAPTER); + ioc->req_sz = MPT_DEFAULT_FRAME_SIZE; /* avoid div by zero! */ + ioc->reply_sz = ioc->req_sz; + + ioc->pcidev = pdev; + ioc->diagPending = 0; + spin_lock_init(&ioc->diagLock); + + /* Initialize the event logging. + */ + ioc->eventTypes = 0; /* None */ + ioc->eventContext = 0; + ioc->eventLogSize = 0; + ioc->events = NULL; + +#ifdef MFCNT + ioc->mfcnt = 0; +#endif + + ioc->cached_fw = NULL; + + /* Initilize SCSI Config Data structure + */ + memset(&ioc->spi_data, 0, sizeof(ScsiCfgData)); + + /* Initialize the running configQ head. + */ + Q_INIT(&ioc->configQ, Q_ITEM); + + /* Find lookup slot. */ + for (ii=0; ii < MPT_MAX_ADAPTERS; ii++) { + if (mpt_adapters[ii] == NULL) { + ioc->id = ii; /* Assign adapter unique id (lookup) */ + break; + } + } + if (ii == MPT_MAX_ADAPTERS) { + printk(KERN_ERR MYNAM ": ERROR - mpt_adapters[%d] table overflow!\n", ii); + kfree(ioc); + return -ENFILE; + } + + mem_phys = msize = 0; + port = psize = 0; + for (ii=0; ii < DEVICE_COUNT_RESOURCE; ii++) { + /* SAE: Why not assume Linux 2.4? */ + if (pci_resource_flags(pdev, ii) & PCI_BASE_ADDRESS_SPACE_IO) { + /* Get I/O space! */ + port = pci_resource_start(pdev, ii); + psize = pci_resource_len(pdev, ii); + } else { + /* Get memmap */ + mem_phys = pci_resource_start(pdev, ii); + msize = pci_resource_len(pdev, ii); + break; + } + } + ioc->mem_size = msize; + + if (ii == DEVICE_COUNT_RESOURCE) { + printk(KERN_ERR MYNAM ": ERROR - MPT adapter has no memory regions defined!\n"); + kfree(ioc); + return -EINVAL; + } + + dprintk((KERN_INFO MYNAM ": MPT adapter @ %lx, msize=%dd bytes\n", mem_phys, msize)); + dprintk((KERN_INFO MYNAM ": (port i/o @ %lx, psize=%dd bytes)\n", port, psize)); + dprintk((KERN_INFO MYNAM ": Using %s register access method\n", PortIo ? "PortIo" : "MemMap")); + + mem = NULL; + if (! PortIo) { + /* Get logical ptr for PciMem0 space */ + /*mem = ioremap(mem_phys, msize);*/ + mem = ioremap(mem_phys, 0x100); + if (mem == NULL) { + printk(KERN_ERR MYNAM ": ERROR - Unable to map adapter memory!\n"); + kfree(ioc); + return -EINVAL; + } + ioc->memmap = mem; + } + dprintk((KERN_INFO MYNAM ": mem = %p, mem_phys = %lx\n", mem, mem_phys)); + + dprintk((KERN_INFO MYNAM ": facts @ %p, pfacts[0] @ %p\n", + &ioc->facts, &ioc->pfacts[0])); + if (PortIo) { + u8 *pmem = (u8*)port; + ioc->mem_phys = port; + ioc->chip = (SYSIF_REGS*)pmem; + } else { + ioc->mem_phys = mem_phys; + ioc->chip = (SYSIF_REGS*)mem; + } + + /* Save Port IO values incase we need to do downloadboot */ + { + u8 *pmem = (u8*)port; + ioc->pio_mem_phys = port; + ioc->pio_chip = (SYSIF_REGS*)pmem; + } + + ioc->chip_type = FCUNK; + if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC909) { + ioc->chip_type = FC909; + ioc->prod_name = "LSIFC909"; + } + else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC929) { + ioc->chip_type = FC929; + ioc->prod_name = "LSIFC929"; + } + else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC919) { + ioc->chip_type = FC919; + ioc->prod_name = "LSIFC919"; + } + else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC929X) { + ioc->chip_type = FC929X; + ioc->prod_name = "LSIFC929X"; + { + /* 929X Chip Fix. Set Split transactions level + * for PCIX. Set bits 5 - 6 to zero, turn on bit 4. + */ + u16 pcixcmd = 0; + pci_read_config_word(pdev, 0x6a, &pcixcmd); + pcixcmd &= 0xFF9F; + pcixcmd |= 0x0010; + pci_write_config_word(pdev, 0x6a, pcixcmd); + } + } + else if (pdev->device == MPI_MANUFACTPAGE_DEVICEID_FC919X) { + ioc->chip_type = FC919X; + ioc->prod_name = "LSIFC919X"; + { + /* 919X Chip Fix. Set Split transactions level + * for PCIX. Set bits 5 - 6 to zero, turn on bit 4. + */ + u16 pcixcmd = 0; + pci_read_config_word(pdev, 0x6a, &pcixcmd); + pcixcmd &= 0xFF9F; + pcixcmd |= 0x0010; + pci_write_config_word(pdev, 0x6a, pcixcmd); + } + } + else if (pdev->device == MPI_MANUFACTPAGE_DEVID_53C1030) { + ioc->chip_type = C1030; + ioc->prod_name = "LSI53C1030"; + { + u8 revision; + + /* 1030 Chip Fix. Disable Split transactions + * for PCIX. Set bits 4 - 6 to zero if Rev < C0( = 8) + */ + pci_read_config_byte(pdev, PCI_CLASS_REVISION, &revision); + if (revision < 0x08) { + u16 pcixcmd = 0; + pci_read_config_word(pdev, 0x6a, &pcixcmd); + pcixcmd &= 0xFF8F; + pci_write_config_word(pdev, 0x6a, pcixcmd); + } + } + } + else if (pdev->device == MPI_MANUFACTPAGE_DEVID_1030_53C1035) { + ioc->chip_type = C1035; + ioc->prod_name = "LSI53C1035"; + } + + sprintf(ioc->name, "ioc%d", ioc->id); + + Q_INIT(&ioc->FreeQ, MPT_FRAME_HDR); + spin_lock_init(&ioc->FreeQlock); + + /* Disable all! */ + CHIPREG_WRITE32(&ioc->chip->IntMask, 0xFFFFFFFF); + ioc->active = 0; + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + + /* tack onto tail of our MPT adapter list */ + Q_ADD_TAIL(&MptAdapters, ioc, MPT_ADAPTER); + + /* Set lookup ptr. */ + mpt_adapters[ioc->id] = ioc; + + ioc->pci_irq = -1; + if (pdev->irq) { + r = request_irq(pdev->irq, mpt_interrupt, SA_SHIRQ, ioc->name, ioc); + + if (r < 0) { +#ifndef __sparc__ + printk(MYIOC_s_ERR_FMT "Unable to allocate interrupt %d!\n", + ioc->name, pdev->irq); +#else + printk(MYIOC_s_ERR_FMT "Unable to allocate interrupt %s!\n", + ioc->name, __irq_itoa(pdev->irq)); +#endif + Q_DEL_ITEM(ioc); + mpt_adapters[ioc->id] = NULL; + iounmap(mem); + kfree(ioc); + return -EBUSY; + } + + ioc->pci_irq = pdev->irq; + + pci_set_master(pdev); /* ?? */ + +#ifndef __sparc__ + dprintk((KERN_INFO MYNAM ": %s installed at interrupt %d\n", ioc->name, pdev->irq)); +#else + dprintk((KERN_INFO MYNAM ": %s installed at interrupt %s\n", ioc->name, __irq_itoa(pdev->irq))); +#endif + } + + /* NEW! 20010220 -sralston + * Check for "bound ports" (929, 929X, 1030, 1035) to reduce redundant resets. + */ + if ((ioc->chip_type == FC929) || (ioc->chip_type == C1030) + || (ioc->chip_type == C1035) || (ioc->chip_type == FC929X)) + mpt_detect_bound_ports(ioc, pdev); + + if ((r = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_BRINGUP, CAN_SLEEP)) != 0) { + printk(KERN_WARNING MYNAM ": WARNING - %s did not initialize properly! (%d)\n", + ioc->name, r); + } + + return r; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_do_ioc_recovery - Initialize or recover MPT adapter. + * @ioc: Pointer to MPT adapter structure + * @reason: Event word / reason + * @sleepFlag: Use schedule if CAN_SLEEP else use udelay. + * + * This routine performs all the steps necessary to bring the IOC + * to a OPERATIONAL state. + * + * This routine also pre-fetches the LAN MAC address of a Fibre Channel + * MPT adapter. + * + * Returns: + * 0 for success + * -1 if failed to get board READY + * -2 if READY but IOCFacts Failed + * -3 if READY but PrimeIOCFifos Failed + * -4 if READY but IOCInit Failed + */ +static int +mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag) +{ + int hard_reset_done = 0; + int alt_ioc_ready = 0; + int hard; + int r; + int ii; + int handlers; + int ret = 0; + int reset_alt_ioc_active = 0; + + printk(KERN_INFO MYNAM ": Initiating %s %s\n", + ioc->name, reason==MPT_HOSTEVENT_IOC_BRINGUP ? "bringup" : "recovery"); + + /* Disable reply interrupts (also blocks FreeQ) */ + CHIPREG_WRITE32(&ioc->chip->IntMask, 0xFFFFFFFF); + ioc->active = 0; + + if (ioc->alt_ioc) { + if (ioc->alt_ioc->active) + reset_alt_ioc_active = 1; + + /* Disable alt-IOC's reply interrupts (and FreeQ) for a bit ... */ + CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask, 0xFFFFFFFF); + ioc->alt_ioc->active = 0; + } + + hard = 1; + if (reason == MPT_HOSTEVENT_IOC_BRINGUP) + hard = 0; + + if ((hard_reset_done = MakeIocReady(ioc, hard, sleepFlag)) < 0) { + if (hard_reset_done == -4) { + printk(KERN_WARNING MYNAM ": %s Owned by PEER..skipping!\n", + ioc->name); + + if (reset_alt_ioc_active && ioc->alt_ioc) { + /* (re)Enable alt-IOC! (reply interrupt, FreeQ) */ + dprintk((KERN_INFO MYNAM ": alt-%s reply irq re-enabled\n", + ioc->alt_ioc->name)); + CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask, ~(MPI_HIM_RIM)); + ioc->alt_ioc->active = 1; + } + + } else { + printk(KERN_WARNING MYNAM ": %s NOT READY WARNING!\n", + ioc->name); + } + return -1; + } + + /* hard_reset_done = 0 if a soft reset was performed + * and 1 if a hard reset was performed. + */ + if (hard_reset_done && reset_alt_ioc_active && ioc->alt_ioc) { + if ((r = MakeIocReady(ioc->alt_ioc, 0, sleepFlag)) == 0) + alt_ioc_ready = 1; + else + printk(KERN_WARNING MYNAM + ": alt-%s: (%d) Not ready WARNING!\n", + ioc->alt_ioc->name, r); + } + + /* Get IOC facts! Allow 1 retry */ + if ((r = GetIocFacts(ioc, sleepFlag, reason)) != 0) + r = GetIocFacts(ioc, sleepFlag, reason); + + if (r) { + ret = -2; + } else if (reason == MPT_HOSTEVENT_IOC_BRINGUP) { + MptDisplayIocCapabilities(ioc); + } + + if (alt_ioc_ready) { + if ((r = GetIocFacts(ioc->alt_ioc, sleepFlag, reason)) != 0) { + /* Retry - alt IOC was initialized once + */ + r = GetIocFacts(ioc->alt_ioc, sleepFlag, reason); + } + if (r) { + alt_ioc_ready = 0; + reset_alt_ioc_active = 0; + } else if (reason == MPT_HOSTEVENT_IOC_BRINGUP) { + MptDisplayIocCapabilities(ioc->alt_ioc); + } + } + + /* Prime reply & request queues! + * (mucho alloc's) Must be done prior to + * init as upper addresses are needed for init. + * If fails, continue with alt-ioc processing + */ + if ((ret == 0) && ((r = PrimeIocFifos(ioc)) != 0)) + ret = -3; + + /* May need to check/upload firmware & data here! + * If fails, continue with alt-ioc processing + */ + if ((ret == 0) && ((r = SendIocInit(ioc, sleepFlag)) != 0)) + ret = -4; +// NEW! + if (alt_ioc_ready && ((r = PrimeIocFifos(ioc->alt_ioc)) != 0)) { + printk(KERN_WARNING MYNAM ": alt-%s: (%d) FIFO mgmt alloc WARNING!\n", + ioc->alt_ioc->name, r); + alt_ioc_ready = 0; + reset_alt_ioc_active = 0; + } + + if (alt_ioc_ready) { + if ((r = SendIocInit(ioc->alt_ioc, sleepFlag)) != 0) { + alt_ioc_ready = 0; + reset_alt_ioc_active = 0; + printk(KERN_WARNING MYNAM + ": alt-%s: (%d) init failure WARNING!\n", + ioc->alt_ioc->name, r); + } + } + + if (reason == MPT_HOSTEVENT_IOC_BRINGUP){ + if (ioc->upload_fw) { + ddlprintk((MYIOC_s_INFO_FMT + "firmware upload required!\n", ioc->name)); + + /* Controller is not operational, cannot do upload + */ + if (ret == 0) { + r = mpt_do_upload(ioc, sleepFlag); + if (r != 0) + printk(KERN_WARNING MYNAM ": firmware upload failure!\n"); + } + + /* Handle the alt IOC too */ + if ((alt_ioc_ready) && (ioc->alt_ioc->upload_fw)){ + ddlprintk((MYIOC_s_INFO_FMT + "Alt-ioc firmware upload required!\n", + ioc->name)); + r = mpt_do_upload(ioc->alt_ioc, sleepFlag); + if (r != 0) + printk(KERN_WARNING MYNAM ": firmware upload failure!\n"); + } + } + } + + if (ret == 0) { + /* Enable! (reply interrupt) */ + CHIPREG_WRITE32(&ioc->chip->IntMask, ~(MPI_HIM_RIM)); + ioc->active = 1; + } + + if (reset_alt_ioc_active && ioc->alt_ioc) { + /* (re)Enable alt-IOC! (reply interrupt) */ + dprintk((KERN_INFO MYNAM ": alt-%s reply irq re-enabled\n", + ioc->alt_ioc->name)); + CHIPREG_WRITE32(&ioc->alt_ioc->chip->IntMask, ~(MPI_HIM_RIM)); + ioc->alt_ioc->active = 1; + } + + /* NEW! 20010120 -sralston + * Enable MPT base driver management of EventNotification + * and EventAck handling. + */ + if ((ret == 0) && (!ioc->facts.EventState)) + (void) SendEventNotification(ioc, 1); /* 1=Enable EventNotification */ + + if (ioc->alt_ioc && alt_ioc_ready && !ioc->alt_ioc->facts.EventState) + (void) SendEventNotification(ioc->alt_ioc, 1); /* 1=Enable EventNotification */ + + /* (Bugzilla:fibrebugs, #513) + * Bug fix (part 2)! 20010905 -sralston + * Add additional "reason" check before call to GetLanConfigPages + * (combined with GetIoUnitPage2 call). This prevents a somewhat + * recursive scenario; GetLanConfigPages times out, timer expired + * routine calls HardResetHandler, which calls into here again, + * and we try GetLanConfigPages again... + */ + if ((ret == 0) && (reason == MPT_HOSTEVENT_IOC_BRINGUP)) { + if ((int)ioc->chip_type <= (int)FC929) { + /* + * Pre-fetch FC port WWN and stuff... + * (FCPortPage0_t stuff) + */ + for (ii=0; ii < ioc->facts.NumberOfPorts; ii++) { + (void) GetFcPortPage0(ioc, ii); + } + + if ((ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) && + (ioc->lan_cnfg_page0.Header.PageLength == 0)) { + /* + * Pre-fetch the ports LAN MAC address! + * (LANPage1_t stuff) + */ + (void) GetLanConfigPages(ioc); +#ifdef MPT_DEBUG + { + u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow; + dprintk((MYIOC_s_INFO_FMT "LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + ioc->name, a[5], a[4], a[3], a[2], a[1], a[0] )); + } +#endif + } + } else { + /* Get NVRAM and adapter maximums from SPP 0 and 2 + */ + mpt_GetScsiPortSettings(ioc, 0); + + /* Get version and length of SDP 1 + */ + mpt_readScsiDevicePageHeaders(ioc, 0); + + /* Find IM volumes + */ + if (ioc->facts.MsgVersion >= 0x0102) + mpt_findImVolumes(ioc); + + /* Check, and possibly reset, the coalescing value + */ + mpt_read_ioc_pg_1(ioc); + } + + GetIoUnitPage2(ioc); + } + + /* + * Call each currently registered protocol IOC reset handler + * with post-reset indication. + * NOTE: If we're doing _IOC_BRINGUP, there can be no + * MptResetHandlers[] registered yet. + */ + if (hard_reset_done) { + r = handlers = 0; + for (ii=MPT_MAX_PROTOCOL_DRIVERS-1; ii; ii--) { + if ((ret == 0) && MptResetHandlers[ii]) { + dprintk((MYIOC_s_INFO_FMT "Calling IOC post_reset handler #%d\n", + ioc->name, ii)); + r += (*(MptResetHandlers[ii]))(ioc, MPT_IOC_POST_RESET); + handlers++; + } + + if (alt_ioc_ready && MptResetHandlers[ii]) { + dprintk((MYIOC_s_INFO_FMT "Calling alt-%s post_reset handler #%d\n", + ioc->name, ioc->alt_ioc->name, ii)); + r += (*(MptResetHandlers[ii]))(ioc->alt_ioc, MPT_IOC_POST_RESET); + handlers++; + } + } + /* FIXME? Examine results here? */ + } + + return ret; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_detect_bound_ports - Search for PCI bus/dev_function + * which matches PCI bus/dev_function (+/-1) for newly discovered 929, + * 929X, 1030 or 1035. + * @ioc: Pointer to MPT adapter structure + * @pdev: Pointer to (struct pci_dev) structure + * + * If match on PCI dev_function +/-1 is found, bind the two MPT adapters + * using alt_ioc pointer fields in their %MPT_ADAPTER structures. + */ +static void +mpt_detect_bound_ports(MPT_ADAPTER *ioc, struct pci_dev *pdev) +{ + MPT_ADAPTER *ioc_srch = mpt_adapter_find_first(); + unsigned int match_lo, match_hi; + + match_lo = pdev->devfn-1; + match_hi = pdev->devfn+1; + dprintk((MYIOC_s_INFO_FMT "PCI bus/devfn=%x/%x, searching for devfn match on %x or %x\n", + ioc->name, pdev->bus->number, pdev->devfn, match_lo, match_hi)); + + while (ioc_srch != NULL) { + struct pci_dev *_pcidev = ioc_srch->pcidev; + + if ((_pcidev->device == pdev->device) && + (_pcidev->bus->number == pdev->bus->number) && + (_pcidev->devfn == match_lo || _pcidev->devfn == match_hi) ) { + /* Paranoia checks */ + if (ioc->alt_ioc != NULL) { + printk(KERN_WARNING MYNAM ": Oops, already bound (%s <==> %s)!\n", + ioc->name, ioc->alt_ioc->name); + break; + } else if (ioc_srch->alt_ioc != NULL) { + printk(KERN_WARNING MYNAM ": Oops, already bound (%s <==> %s)!\n", + ioc_srch->name, ioc_srch->alt_ioc->name); + break; + } + dprintk((KERN_INFO MYNAM ": FOUND! binding %s <==> %s\n", + ioc->name, ioc_srch->name)); + ioc_srch->alt_ioc = ioc; + ioc->alt_ioc = ioc_srch; + break; + } + ioc_srch = mpt_adapter_find_next(ioc_srch); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_adapter_disable - Disable misbehaving MPT adapter. + * @this: Pointer to MPT adapter structure + * @free: Free up alloc'd reply, request, etc. + */ +static void +mpt_adapter_disable(MPT_ADAPTER *this, int freeup) +{ + if (this != NULL) { + int sz; + u32 state; + int ret; + + /* Disable the FW */ + state = mpt_GetIocState(this, 1); + if (state == MPI_IOC_STATE_OPERATIONAL) { + SendIocReset(this, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, NO_SLEEP); + } + + if (this->cached_fw != NULL) { + ddlprintk((KERN_INFO MYNAM ": Pushing FW onto adapter\n")); + + if ((ret = mpt_downloadboot(this, NO_SLEEP)) < 0) { + printk(KERN_WARNING MYNAM + ": firmware downloadboot failure (%d)!\n", ret); + } + } + + /* Disable adapter interrupts! */ + CHIPREG_WRITE32(&this->chip->IntMask, 0xFFFFFFFF); + this->active = 0; + /* Clear any lingering interrupt */ + CHIPREG_WRITE32(&this->chip->IntStatus, 0); + + if (freeup && this->reply_alloc != NULL) { + sz = (this->reply_sz * this->reply_depth) + 128; + pci_free_consistent(this->pcidev, sz, + this->reply_alloc, this->reply_alloc_dma); + this->reply_frames = NULL; + this->reply_alloc = NULL; + this->alloc_total -= sz; + } + + if (freeup && this->req_alloc != NULL) { + sz = (this->req_sz * this->req_depth) + 128; + /* + * Rounding UP to nearest 4-kB boundary here... + */ + sz = ((sz + 0x1000UL - 1UL) / 0x1000) * 0x1000; + pci_free_consistent(this->pcidev, sz, + this->req_alloc, this->req_alloc_dma); + this->req_frames = NULL; + this->req_alloc = NULL; + this->alloc_total -= sz; + } + + if (freeup && this->sense_buf_pool != NULL) { + sz = (this->req_depth * MPT_SENSE_BUFFER_ALLOC); + pci_free_consistent(this->pcidev, sz, + this->sense_buf_pool, this->sense_buf_pool_dma); + this->sense_buf_pool = NULL; + this->alloc_total -= sz; + } + + if (freeup && this->events != NULL){ + sz = MPTCTL_EVENT_LOG_SIZE * sizeof(MPT_IOCTL_EVENTS); + kfree(this->events); + this->events = NULL; + this->alloc_total -= sz; + } + + if (freeup && this->cached_fw != NULL) { + int ii = 0; + + while ((ii < this->num_fw_frags) && (this->cached_fw[ii]!= NULL)) { + sz = this->cached_fw[ii]->size; + pci_free_consistent(this->pcidev, sz, + this->cached_fw[ii]->fw, this->cached_fw[ii]->fw_dma); + this->cached_fw[ii]->fw = NULL; + this->alloc_total -= sz; + + kfree(this->cached_fw[ii]); + this->cached_fw[ii] = NULL; + this->alloc_total -= sizeof(fw_image_t); + + ii++; + } + + kfree(this->cached_fw); + this->cached_fw = NULL; + sz = this->num_fw_frags * sizeof(void *); + this->alloc_total -= sz; + } + + if (freeup && this->spi_data.nvram != NULL) { + kfree(this->spi_data.nvram); + this->spi_data.nvram = NULL; + } + + if (freeup && this->spi_data.pIocPg3 != NULL) { + kfree(this->spi_data.pIocPg3); + this->spi_data.pIocPg3 = NULL; + } + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_adapter_dispose - Free all resources associated with a MPT + * adapter. + * @this: Pointer to MPT adapter structure + * + * This routine unregisters h/w resources and frees all alloc'd memory + * associated with a MPT adapter structure. + */ +static void +mpt_adapter_dispose(MPT_ADAPTER *this) +{ + if (this != NULL) { + int sz_first, sz_last; + + sz_first = this->alloc_total; + + if (this->alt_ioc != NULL) { + this->alt_ioc->alt_ioc = NULL; + this->alt_ioc = NULL; + } + + mpt_adapter_disable(this, 1); + + if (this->pci_irq != -1) { + free_irq(this->pci_irq, this); + this->pci_irq = -1; + } + + if (this->memmap != NULL) + iounmap((u8 *) this->memmap); + +#if defined(CONFIG_MTRR) && 0 + if (this->mtrr_reg > 0) { + mtrr_del(this->mtrr_reg, 0, 0); + dprintk((KERN_INFO MYNAM ": %s: MTRR region de-registered\n", this->name)); + } +#endif + + /* Zap the adapter lookup ptr! */ + mpt_adapters[this->id] = NULL; + + sz_last = this->alloc_total; + dprintk((KERN_INFO MYNAM ": %s: free'd %d of %d bytes\n", + this->name, sz_first-sz_last+(int)sizeof(*this), sz_first)); + kfree(this); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * MptDisplayIocCapabilities - Disply IOC's capacilities. + * @ioc: Pointer to MPT adapter structure + */ +static void +MptDisplayIocCapabilities(MPT_ADAPTER *ioc) +{ + int i = 0; + + printk(KERN_INFO "%s: ", ioc->name); + if (ioc->prod_name && strlen(ioc->prod_name) > 3) + printk("%s: ", ioc->prod_name+3); + printk("Capabilities={"); + + if (ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_INITIATOR) { + printk("Initiator"); + i++; + } + + if (ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_TARGET) { + printk("%sTarget", i ? "," : ""); + i++; + } + + if (ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) { + printk("%sLAN", i ? "," : ""); + i++; + } + +#if 0 + /* + * This would probably evoke more questions than it's worth + */ + if (ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_TARGET) { + printk("%sLogBusAddr", i ? "," : ""); + i++; + } +#endif + + printk("}\n"); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * MakeIocReady - Get IOC to a READY state, using KickStart if needed. + * @ioc: Pointer to MPT_ADAPTER structure + * @force: Force hard KickStart of IOC + * @sleepFlag: Specifies whether the process can sleep + * + * Returns: + * 1 - DIAG reset and READY + * 0 - READY initially OR soft reset and READY + * -1 - Any failure on KickStart + * -2 - Msg Unit Reset Failed + * -3 - IO Unit Reset Failed + * -4 - IOC owned by a PEER + */ +static int +MakeIocReady(MPT_ADAPTER *ioc, int force, int sleepFlag) +{ + u32 ioc_state; + int statefault = 0; + int cntdn; + int hard_reset_done = 0; + int r; + int ii; + int whoinit; + + /* Get current [raw] IOC state */ + ioc_state = mpt_GetIocState(ioc, 0); + dhsprintk((KERN_INFO MYNAM "::MakeIocReady, %s [raw] state=%08x\n", ioc->name, ioc_state)); + + /* + * Check to see if IOC got left/stuck in doorbell handshake + * grip of death. If so, hard reset the IOC. + */ + if (ioc_state & MPI_DOORBELL_ACTIVE) { + statefault = 1; + printk(MYIOC_s_WARN_FMT "Unexpected doorbell active!\n", + ioc->name); + } + + /* Is it already READY? */ + if (!statefault && (ioc_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_READY) { + if ((int)ioc->chip_type <= (int)FC929) + return 0; + else { + /* Workaround from broken 1030 FW. + * Force a diagnostic reset if fails. + */ + if ((r = SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, sleepFlag)) == 0) + return 0; + else + statefault = 4; + } + } + + /* + * Check to see if IOC is in FAULT state. + */ + if ((ioc_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) { + statefault = 2; + printk(MYIOC_s_WARN_FMT "IOC is in FAULT state!!!\n", + ioc->name); + printk(KERN_WARNING " FAULT code = %04xh\n", + ioc_state & MPI_DOORBELL_DATA_MASK); + } + + /* + * Hmmm... Did it get left operational? + */ + if ((ioc_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_OPERATIONAL) { + dprintk((MYIOC_s_WARN_FMT "IOC operational unexpected\n", + ioc->name)); + + /* Check WhoInit. + * If PCI Peer, exit. + * Else, if no fault conditions are present, issue a MessageUnitReset + * Else, fall through to KickStart case + */ + whoinit = (ioc_state & MPI_DOORBELL_WHO_INIT_MASK) >> MPI_DOORBELL_WHO_INIT_SHIFT; + dprintk((KERN_WARNING MYNAM + ": whoinit 0x%x\n statefault %d force %d\n", + whoinit, statefault, force)); + if (whoinit == MPI_WHOINIT_PCI_PEER) + return -4; + else { + if ((statefault == 0 ) && (force == 0)) { + if ((r = SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, sleepFlag)) == 0) + return 0; + } + statefault = 3; + } + } + + hard_reset_done = KickStart(ioc, statefault||force, sleepFlag); + if (hard_reset_done < 0) + return -1; + + /* + * Loop here waiting for IOC to come READY. + */ + ii = 0; + cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * 15; /* 15 seconds */ + + while ((ioc_state = mpt_GetIocState(ioc, 1)) != MPI_IOC_STATE_READY) { + if (ioc_state == MPI_IOC_STATE_OPERATIONAL) { + /* + * BIOS or previous driver load left IOC in OP state. + * Reset messaging FIFOs. + */ + if ((r = SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, sleepFlag)) != 0) { + printk(MYIOC_s_ERR_FMT "IOC msg unit reset failed!\n", ioc->name); + return -2; + } + } else if (ioc_state == MPI_IOC_STATE_RESET) { + /* + * Something is wrong. Try to get IOC back + * to a known state. + */ + if ((r = SendIocReset(ioc, MPI_FUNCTION_IO_UNIT_RESET, sleepFlag)) != 0) { + printk(MYIOC_s_ERR_FMT "IO unit reset failed!\n", ioc->name); + return -3; + } + } + + ii++; cntdn--; + if (!cntdn) { + printk(MYIOC_s_ERR_FMT "Wait IOC_READY state timeout(%d)!\n", + ioc->name, (ii+5)/HZ); + return -ETIME; + } + + if (sleepFlag == CAN_SLEEP) { +/* SAE: Can't do this in the hypervisor */ +#if XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); +#else + mdelay(1); +#endif + } else { + mdelay (1); /* 1 msec delay */ + } + + } + + if (statefault < 3) { + printk(MYIOC_s_INFO_FMT "Recovered from %s\n", + ioc->name, + statefault==1 ? "stuck handshake" : "IOC FAULT"); + } + + return hard_reset_done; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_GetIocState - Get the current state of a MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * @cooked: Request raw or cooked IOC state + * + * Returns all IOC Doorbell register bits if cooked==0, else just the + * Doorbell bits in MPI_IOC_STATE_MASK. + */ +u32 +mpt_GetIocState(MPT_ADAPTER *ioc, int cooked) +{ + u32 s, sc; + + /* Get! */ + s = CHIPREG_READ32(&ioc->chip->Doorbell); +// dprintk((MYIOC_s_INFO_FMT "raw state = %08x\n", ioc->name, s)); + sc = s & MPI_IOC_STATE_MASK; + + /* Save! */ + ioc->last_state = sc; + + return cooked ? sc : s; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * GetIocFacts - Send IOCFacts request to MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * @sleepFlag: Specifies whether the process can sleep + * @reason: If recovery, only update facts. + * + * Returns 0 for success, non-zero for failure. + */ +static int +GetIocFacts(MPT_ADAPTER *ioc, int sleepFlag, int reason) +{ + IOCFacts_t get_facts; + IOCFactsReply_t *facts; + int r; + int req_sz; + int reply_sz; + u32 status; + + /* IOC *must* NOT be in RESET state! */ + if (ioc->last_state == MPI_IOC_STATE_RESET) { + printk(KERN_ERR MYNAM ": ERROR - Can't get IOCFacts, %s NOT READY! (%08x)\n", + ioc->name, + ioc->last_state ); + return -44; + } + + facts = &ioc->facts; + + /* Destination (reply area)... */ + reply_sz = sizeof(*facts); + memset(facts, 0, reply_sz); + + /* Request area (get_facts on the stack right now!) */ + req_sz = sizeof(get_facts); + memset(&get_facts, 0, req_sz); + + get_facts.Function = MPI_FUNCTION_IOC_FACTS; + /* Assert: All other get_facts fields are zero! */ + + dprintk((MYIOC_s_INFO_FMT "Sending get IocFacts request\n", ioc->name)); + + /* No non-zero fields in the get_facts request are greater than + * 1 byte in size, so we can just fire it off as is. + */ + r = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&get_facts, + reply_sz, (u16*)facts, 3 /*seconds*/, sleepFlag); + if (r != 0) + return r; + + /* + * Now byte swap (GRRR) the necessary fields before any further + * inspection of reply contents. + * + * But need to do some sanity checks on MsgLength (byte) field + * to make sure we don't zero IOC's req_sz! + */ + /* Did we get a valid reply? */ + if (facts->MsgLength > offsetof(IOCFactsReply_t, RequestFrameSize)/sizeof(u32)) { + if (reason == MPT_HOSTEVENT_IOC_BRINGUP) { + /* + * If not been here, done that, save off first WhoInit value + */ + if (ioc->FirstWhoInit == WHOINIT_UNKNOWN) + ioc->FirstWhoInit = facts->WhoInit; + } + + facts->MsgVersion = le16_to_cpu(facts->MsgVersion); + facts->MsgContext = le32_to_cpu(facts->MsgContext); + facts->IOCExceptions = le16_to_cpu(facts->IOCExceptions); + facts->IOCStatus = le16_to_cpu(facts->IOCStatus); + facts->IOCLogInfo = le32_to_cpu(facts->IOCLogInfo); + status = facts->IOCStatus & MPI_IOCSTATUS_MASK; + /* CHECKME! IOCStatus, IOCLogInfo */ + + facts->ReplyQueueDepth = le16_to_cpu(facts->ReplyQueueDepth); + facts->RequestFrameSize = le16_to_cpu(facts->RequestFrameSize); + + /* + * FC f/w version changed between 1.1 and 1.2 + * Old: u16{Major(4),Minor(4),SubMinor(8)} + * New: u32{Major(8),Minor(8),Unit(8),Dev(8)} + */ + if (facts->MsgVersion < 0x0102) { + /* + * Handle old FC f/w style, convert to new... + */ + u16 oldv = le16_to_cpu(facts->Reserved_0101_FWVersion); + facts->FWVersion.Word = + ((oldv<<12) & 0xFF000000) | + ((oldv<<8) & 0x000FFF00); + } else + facts->FWVersion.Word = le32_to_cpu(facts->FWVersion.Word); + + facts->ProductID = le16_to_cpu(facts->ProductID); + facts->CurrentHostMfaHighAddr = + le32_to_cpu(facts->CurrentHostMfaHighAddr); + facts->GlobalCredits = le16_to_cpu(facts->GlobalCredits); + facts->CurrentSenseBufferHighAddr = + le32_to_cpu(facts->CurrentSenseBufferHighAddr); + facts->CurReplyFrameSize = + le16_to_cpu(facts->CurReplyFrameSize); + + /* + * Handle NEW (!) IOCFactsReply fields in MPI-1.01.xx + * Older MPI-1.00.xx struct had 13 dwords, and enlarged + * to 14 in MPI-1.01.0x. + */ + if (facts->MsgLength >= (offsetof(IOCFactsReply_t,FWImageSize) + 7)/4 && + facts->MsgVersion > 0x0100) { + facts->FWImageSize = le32_to_cpu(facts->FWImageSize); + } + + if (!facts->RequestFrameSize) { + /* Something is wrong! */ + printk(MYIOC_s_ERR_FMT "IOC reported invalid 0 request size!\n", + ioc->name); + return -55; + } + + if (reason == MPT_HOSTEVENT_IOC_BRINGUP) { + /* + * Set values for this IOC's request & reply frame sizes, + * and request & reply queue depths... + */ + ioc->req_sz = MIN(MPT_DEFAULT_FRAME_SIZE, facts->RequestFrameSize * 4); + ioc->req_depth = MIN(MPT_MAX_REQ_DEPTH, facts->GlobalCredits); + ioc->reply_sz = ioc->req_sz; + ioc->reply_depth = MIN(MPT_DEFAULT_REPLY_DEPTH, facts->ReplyQueueDepth); + + dprintk((MYIOC_s_INFO_FMT "reply_sz=%3d, reply_depth=%4d\n", + ioc->name, ioc->reply_sz, ioc->reply_depth)); + dprintk((MYIOC_s_INFO_FMT "req_sz =%3d, req_depth =%4d\n", + ioc->name, ioc->req_sz, ioc->req_depth)); + + /* Get port facts! */ + if ( (r = GetPortFacts(ioc, 0, sleepFlag)) != 0 ) + return r; + } + } else { + printk(MYIOC_s_ERR_FMT "Invalid IOC facts reply!\n", + ioc->name); + return -66; + } + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * GetPortFacts - Send PortFacts request to MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * @portnum: Port number + * @sleepFlag: Specifies whether the process can sleep + * + * Returns 0 for success, non-zero for failure. + */ +static int +GetPortFacts(MPT_ADAPTER *ioc, int portnum, int sleepFlag) +{ + PortFacts_t get_pfacts; + PortFactsReply_t *pfacts; + int ii; + int req_sz; + int reply_sz; + + /* IOC *must* NOT be in RESET state! */ + if (ioc->last_state == MPI_IOC_STATE_RESET) { + printk(KERN_ERR MYNAM ": ERROR - Can't get PortFacts, %s NOT READY! (%08x)\n", + ioc->name, + ioc->last_state ); + return -4; + } + + pfacts = &ioc->pfacts[portnum]; + + /* Destination (reply area)... */ + reply_sz = sizeof(*pfacts); + memset(pfacts, 0, reply_sz); + + /* Request area (get_pfacts on the stack right now!) */ + req_sz = sizeof(get_pfacts); + memset(&get_pfacts, 0, req_sz); + + get_pfacts.Function = MPI_FUNCTION_PORT_FACTS; + get_pfacts.PortNumber = portnum; + /* Assert: All other get_pfacts fields are zero! */ + + dprintk((MYIOC_s_INFO_FMT "Sending get PortFacts(%d) request\n", + ioc->name, portnum)); + + /* No non-zero fields in the get_pfacts request are greater than + * 1 byte in size, so we can just fire it off as is. + */ + ii = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&get_pfacts, + reply_sz, (u16*)pfacts, 3 /*seconds*/, sleepFlag); + if (ii != 0) + return ii; + + /* Did we get a valid reply? */ + + /* Now byte swap the necessary fields in the response. */ + pfacts->MsgContext = le32_to_cpu(pfacts->MsgContext); + pfacts->IOCStatus = le16_to_cpu(pfacts->IOCStatus); + pfacts->IOCLogInfo = le32_to_cpu(pfacts->IOCLogInfo); + pfacts->MaxDevices = le16_to_cpu(pfacts->MaxDevices); + pfacts->PortSCSIID = le16_to_cpu(pfacts->PortSCSIID); + pfacts->ProtocolFlags = le16_to_cpu(pfacts->ProtocolFlags); + pfacts->MaxPostedCmdBuffers = le16_to_cpu(pfacts->MaxPostedCmdBuffers); + pfacts->MaxPersistentIDs = le16_to_cpu(pfacts->MaxPersistentIDs); + pfacts->MaxLanBuckets = le16_to_cpu(pfacts->MaxLanBuckets); + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * SendIocInit - Send IOCInit request to MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * @sleepFlag: Specifies whether the process can sleep + * + * Send IOCInit followed by PortEnable to bring IOC to OPERATIONAL state. + * + * Returns 0 for success, non-zero for failure. + */ +static int +SendIocInit(MPT_ADAPTER *ioc, int sleepFlag) +{ + IOCInit_t ioc_init; + MPIDefaultReply_t init_reply; + u32 state; + int r; + int count; + int cntdn; + + memset(&ioc_init, 0, sizeof(ioc_init)); + memset(&init_reply, 0, sizeof(init_reply)); + + ioc_init.WhoInit = MPI_WHOINIT_HOST_DRIVER; +/* ioc_init.ChainOffset = 0; */ + ioc_init.Function = MPI_FUNCTION_IOC_INIT; +/* ioc_init.Flags = 0; */ + + /* If we are in a recovery mode and we uploaded the FW image, + * then this pointer is not NULL. Skip the upload a second time. + * Set this flag if cached_fw set for either IOC. + */ + ioc->upload_fw = 0; + ioc_init.Flags = 0; + if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) { + if ((ioc->cached_fw) || (ioc->alt_ioc && ioc->alt_ioc->cached_fw)) + ioc_init.Flags = MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE; + else + ioc->upload_fw = 1; + } + ddlprintk((MYIOC_s_INFO_FMT "flags %d, upload_fw %d \n", + ioc->name, ioc_init.Flags, ioc->upload_fw)); + + if ((int)ioc->chip_type <= (int)FC929) { + ioc_init.MaxDevices = MPT_MAX_FC_DEVICES; + } else { + ioc_init.MaxDevices = MPT_MAX_SCSI_DEVICES; + } + ioc_init.MaxBuses = MPT_MAX_BUS; + +/* ioc_init.MsgFlags = 0; */ +/* ioc_init.MsgContext = cpu_to_le32(0x00000000); */ + ioc_init.ReplyFrameSize = cpu_to_le16(ioc->reply_sz); /* in BYTES */ + + if (sizeof(dma_addr_t) == sizeof(u64)) { + /* Save the upper 32-bits of the request + * (reply) and sense buffers. + */ + ioc_init.HostMfaHighAddr = cpu_to_le32((u32)((u64)ioc->req_frames_dma >> 32)); + ioc_init.SenseBufferHighAddr = cpu_to_le32((u32)((u64)ioc->sense_buf_pool_dma >> 32)); + } else { + /* Force 32-bit addressing */ + ioc_init.HostMfaHighAddr = cpu_to_le32(0); + ioc_init.SenseBufferHighAddr = cpu_to_le32(0); + } + + dprintk((MYIOC_s_INFO_FMT "Sending IOCInit (req @ %p)\n", + ioc->name, &ioc_init)); + + r = mpt_handshake_req_reply_wait(ioc, sizeof(IOCInit_t), (u32*)&ioc_init, + sizeof(MPIDefaultReply_t), (u16*)&init_reply, 10 /*seconds*/, sleepFlag); + if (r != 0) + return r; + + /* No need to byte swap the multibyte fields in the reply + * since we don't even look at it's contents. + */ + + if ((r = SendPortEnable(ioc, 0, sleepFlag)) != 0) + return r; + + /* YIKES! SUPER IMPORTANT!!! + * Poll IocState until _OPERATIONAL while IOC is doing + * LoopInit and TargetDiscovery! + */ + count = 0; + cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * 60; /* 60 seconds */ + state = mpt_GetIocState(ioc, 1); + while (state != MPI_IOC_STATE_OPERATIONAL && --cntdn) { + if (sleepFlag == CAN_SLEEP) { +/* SAE: Can't do this in the hypervisor */ +#if XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); +#else + mdelay(1); +#endif + } else { + mdelay(1); + } + + if (!cntdn) { + printk(MYIOC_s_ERR_FMT "Wait IOC_OP state timeout(%d)!\n", + ioc->name, (count+5)/HZ); + return -9; + } + + state = mpt_GetIocState(ioc, 1); + count++; + } + dhsprintk((MYIOC_s_INFO_FMT "INFO - Wait IOC_OPERATIONAL state (cnt=%d)\n", + ioc->name, count)); + + return r; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * SendPortEnable - Send PortEnable request to MPT adapter port. + * @ioc: Pointer to MPT_ADAPTER structure + * @portnum: Port number to enable + * @sleepFlag: Specifies whether the process can sleep + * + * Send PortEnable to bring IOC to OPERATIONAL state. + * + * Returns 0 for success, non-zero for failure. + */ +static int +SendPortEnable(MPT_ADAPTER *ioc, int portnum, int sleepFlag) +{ + PortEnable_t port_enable; + MPIDefaultReply_t reply_buf; + int ii; + int req_sz; + int reply_sz; + + /* Destination... */ + reply_sz = sizeof(MPIDefaultReply_t); + memset(&reply_buf, 0, reply_sz); + + req_sz = sizeof(PortEnable_t); + memset(&port_enable, 0, req_sz); + + port_enable.Function = MPI_FUNCTION_PORT_ENABLE; + port_enable.PortNumber = portnum; +/* port_enable.ChainOffset = 0; */ +/* port_enable.MsgFlags = 0; */ +/* port_enable.MsgContext = 0; */ + + dprintk((MYIOC_s_INFO_FMT "Sending Port(%d)Enable (req @ %p)\n", + ioc->name, portnum, &port_enable)); + + /* RAID FW may take a long time to enable + */ + if ((int)ioc->chip_type <= (int)FC929) { + ii = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&port_enable, + reply_sz, (u16*)&reply_buf, 65 /*seconds*/, sleepFlag); + } else { + ii = mpt_handshake_req_reply_wait(ioc, req_sz, (u32*)&port_enable, + reply_sz, (u16*)&reply_buf, 300 /*seconds*/, sleepFlag); + } + + if (ii != 0) + return ii; + + /* We do not even look at the reply, so we need not + * swap the multi-byte fields. + */ + + return 0; +} + +/* + * Inputs: size - total FW bytes + * Outputs: frags - number of fragments needed + * Return NULL if failed. + */ +void * +mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size, int *frags, int *alloc_sz) +{ + fw_image_t **cached_fw = NULL; + u8 *mem = NULL; + dma_addr_t fw_dma; + int alloc_total = 0; + int bytes_left, bytes, num_frags; + int sz, ii; + + /* cached_fw + */ + sz = ioc->num_fw_frags * sizeof(void *); + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) + return NULL; + + memset(mem, 0, sz); + cached_fw = (fw_image_t **)mem; + alloc_total += sz; + + /* malloc fragment memory + * fw_image_t struct and dma for fw data + */ + bytes_left = size; + ii = 0; + num_frags = 0; + bytes = bytes_left; + while((bytes_left) && (num_frags < ioc->num_fw_frags)) { + if (cached_fw[ii] == NULL) { + mem = kmalloc(sizeof(fw_image_t), GFP_ATOMIC); + if (mem == NULL) + break; + + memset(mem, 0, sizeof(fw_image_t)); + cached_fw[ii] = (fw_image_t *)mem; + alloc_total += sizeof(fw_image_t); + } + + mem = pci_alloc_consistent(ioc->pcidev, bytes, &fw_dma); + if (mem == NULL) { + if (bytes > 0x10000) + bytes = 0x10000; + else if (bytes > 0x8000) + bytes = 0x8000; + else if (bytes > 0x4000) + bytes = 0x4000; + else if (bytes > 0x2000) + bytes = 0x2000; + else if (bytes > 0x1000) + bytes = 0x1000; + else + break; + + continue; + } + + cached_fw[ii]->fw = mem; + cached_fw[ii]->fw_dma = fw_dma; + cached_fw[ii]->size = bytes; + memset(mem, 0, bytes); + alloc_total += bytes; + + bytes_left -= bytes; + + num_frags++; + ii++; + } + + if (bytes_left ) { + /* Major Failure. + */ + mpt_free_fw_memory(ioc, cached_fw); + return NULL; + } + + *frags = num_frags; + *alloc_sz = alloc_total; + + return (void *) cached_fw; +} + +/* + * If alt_img is NULL, delete from ioc structure. + * Else, delete a secondary image in same format. + */ +void +mpt_free_fw_memory(MPT_ADAPTER *ioc, fw_image_t **alt_img) +{ + fw_image_t **cached_fw; + int ii; + int sz; + int alloc_freed = 0; + + if (alt_img != NULL) + cached_fw = alt_img; + else + cached_fw = ioc->cached_fw; + + if (cached_fw == NULL) + return; + + ii = 0; + while ((ii < ioc->num_fw_frags) && (cached_fw[ii]!= NULL)) { + sz = cached_fw[ii]->size; + if (sz > 0) { + pci_free_consistent(ioc->pcidev, sz, + cached_fw[ii]->fw, cached_fw[ii]->fw_dma); + } + cached_fw[ii]->fw = NULL; + alloc_freed += sz; + + kfree(cached_fw[ii]); + cached_fw[ii] = NULL; + alloc_freed += sizeof(fw_image_t); + + ii++; + } + + kfree(cached_fw); + cached_fw = NULL; + sz = ioc->num_fw_frags * sizeof(void *); + alloc_freed += sz; + + if (alt_img == NULL) + ioc->alloc_total -= alloc_freed; + + return; +} + + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_do_upload - Construct and Send FWUpload request to MPT adapter port. + * @ioc: Pointer to MPT_ADAPTER structure + * @sleepFlag: Specifies whether the process can sleep + * + * Returns 0 for success, >0 for handshake failure + * <0 for fw upload failure. + * + * Remark: If bound IOC and a successful FWUpload was performed + * on the bound IOC, the second image is discarded + * and memory is free'd. Both channels must upload to prevent + * IOC from running in degraded mode. + */ +static int +mpt_do_upload(MPT_ADAPTER *ioc, int sleepFlag) +{ + u8 request[ioc->req_sz]; + u8 reply[sizeof(FWUploadReply_t)]; + FWUpload_t *prequest; + FWUploadReply_t *preply; + FWUploadTCSGE_t *ptcsge = NULL; + int sgeoffset; + int ii, sz, reply_sz; + int cmdStatus, freeMem = 0; + int num_frags, alloc_sz; + + /* If the image size is 0 or if the pointer is + * not NULL (error), we are done. + */ + if (((sz = ioc->facts.FWImageSize) == 0) || ioc->cached_fw) + return 0; + + ioc->num_fw_frags = ioc->req_sz - sizeof(FWUpload_t) + sizeof(dma_addr_t) + sizeof(u32) -1; + ioc->num_fw_frags /= sizeof(dma_addr_t) + sizeof(u32); + + ioc->cached_fw = (fw_image_t **) mpt_alloc_fw_memory(ioc, + ioc->facts.FWImageSize, &num_frags, &alloc_sz); + + if (ioc->cached_fw == NULL) { + /* Major Failure. + */ + mpt_free_fw_memory(ioc, NULL); + ioc->cached_fw = NULL; + + return -ENOMEM; + } + ioc->alloc_total += alloc_sz; + + ddlprintk((KERN_INFO MYNAM ": FW Image @ %p, sz=%d bytes\n", + (void *)(ulong)ioc->cached_fw, ioc->facts.FWImageSize)); + + prequest = (FWUpload_t *)&request; + preply = (FWUploadReply_t *)&reply; + + /* Destination... */ + memset(prequest, 0, ioc->req_sz); + + reply_sz = sizeof(reply); + memset(preply, 0, reply_sz); + + prequest->ImageType = MPI_FW_UPLOAD_ITYPE_FW_IOC_MEM; + prequest->Function = MPI_FUNCTION_FW_UPLOAD; + prequest->MsgContext = 0; /* anything */ + + ptcsge = (FWUploadTCSGE_t *) &prequest->SGL; + ptcsge->Reserved = 0; + ptcsge->ContextSize = 0; + ptcsge->DetailsLength = 12; + ptcsge->Flags = MPI_SGE_FLAGS_TRANSACTION_ELEMENT; + ptcsge->Reserved1 = 0; + ptcsge->ImageOffset = 0; + ptcsge->ImageSize = cpu_to_le32(sz); + + sgeoffset = sizeof(FWUpload_t) - sizeof(SGE_MPI_UNION) + sizeof(FWUploadTCSGE_t); + + for (ii = 0; ii < (num_frags-1); ii++) { + mpt_add_sge(&request[sgeoffset], MPT_SGE_FLAGS_SIMPLE_ELEMENT | + MPT_SGE_FLAGS_ADDRESSING | MPT_TRANSFER_IOC_TO_HOST | + (u32) ioc->cached_fw[ii]->size, ioc->cached_fw[ii]->fw_dma); + + sgeoffset += sizeof(u32) + sizeof(dma_addr_t); + } + + mpt_add_sge(&request[sgeoffset], + MPT_SGE_FLAGS_SSIMPLE_READ |(u32) ioc->cached_fw[ii]->size, + ioc->cached_fw[ii]->fw_dma); + + sgeoffset += sizeof(u32) + sizeof(dma_addr_t); + + dprintk((MYIOC_s_INFO_FMT "Sending FW Upload (req @ %p) size %d \n", + ioc->name, prequest, sgeoffset)); + + ii = mpt_handshake_req_reply_wait(ioc, sgeoffset, (u32*)prequest, + reply_sz, (u16*)preply, 65 /*seconds*/, sleepFlag); + + cmdStatus = -EFAULT; + if (ii == 0) { + /* Handshake transfer was complete and successful. + * Check the Reply Frame. + */ + int status, transfer_sz; + status = le16_to_cpu(preply->IOCStatus); + if (status == MPI_IOCSTATUS_SUCCESS) { + transfer_sz = le32_to_cpu(preply->ActualImageSize); + if (transfer_sz == sz) + cmdStatus = 0; + } + } + ddlprintk((MYIOC_s_INFO_FMT ": do_upload status %d \n", + ioc->name, cmdStatus)); + + /* Check to see if we have a copy of this image in + * host memory already. + */ + if (cmdStatus == 0) { + ioc->upload_fw = 0; + if (ioc->alt_ioc && ioc->alt_ioc->cached_fw) + freeMem = 1; + } + + /* We already have a copy of this image or + * we had some type of an error - either the handshake + * failed (i != 0) or the command did not complete successfully. + */ + if (cmdStatus || freeMem) { + + ddlprintk((MYIOC_s_INFO_FMT ": do_upload freeing %s image \n", + ioc->name, cmdStatus ? "incomplete" : "duplicate")); + mpt_free_fw_memory(ioc, NULL); + ioc->cached_fw = NULL; + } + + return cmdStatus; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_downloadboot - DownloadBoot code + * @ioc: Pointer to MPT_ADAPTER structure + * @flag: Specify which part of IOC memory is to be uploaded. + * @sleepFlag: Specifies whether the process can sleep + * + * FwDownloadBoot requires Programmed IO access. + * + * Returns 0 for success + * -1 FW Image size is 0 + * -2 No valid cached_fw Pointer + * <0 for fw upload failure. + */ +static int +mpt_downloadboot(MPT_ADAPTER *ioc, int sleepFlag) +{ + MpiFwHeader_t *FwHdr = NULL; + MpiExtImageHeader_t *ExtHdr; + fw_image_t **pCached = NULL; + int fw_sz; + u32 diag0val; +#ifdef MPT_DEBUG + u32 diag1val = 0; +#endif + int count = 0; + u32 *ptru32 = NULL; + u32 diagRwData; + u32 nextImage; + u32 ext_offset; + u32 load_addr; + int max_idx, fw_idx, ext_idx; + int left_u32s; + + ddlprintk((MYIOC_s_INFO_FMT "DbGb0: downloadboot entered.\n", + ioc->name)); +#ifdef MPT_DEBUG + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + if (ioc->alt_ioc) + diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); + ddlprintk((MYIOC_s_INFO_FMT "DbGb1: diag0=%08x, diag1=%08x\n", + ioc->name, diag0val, diag1val)); +#endif + + ddlprintk((MYIOC_s_INFO_FMT "fw size 0x%x, ioc FW Ptr %p\n", + ioc->name, ioc->facts.FWImageSize, ioc->cached_fw)); + if (ioc->alt_ioc) + ddlprintk((MYIOC_s_INFO_FMT "alt ioc FW Ptr %p\n", + ioc->name, ioc->alt_ioc->cached_fw)); + + /* Get dma_addr and data transfer size. + */ + if ((fw_sz = ioc->facts.FWImageSize) == 0) + return -1; + + /* Get the DMA from ioc or ioc->alt_ioc */ + if (ioc->cached_fw != NULL) + pCached = (fw_image_t **)ioc->cached_fw; + else if (ioc->alt_ioc && (ioc->alt_ioc->cached_fw != NULL)) + pCached = (fw_image_t **)ioc->alt_ioc->cached_fw; + + ddlprintk((MYIOC_s_INFO_FMT "DbGb2: FW Image @ %p\n", + ioc->name, pCached)); + if (!pCached) + return -2; + + /* Write magic sequence to WriteSequence register + * until enter diagnostic mode + */ + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + while ((diag0val & MPI_DIAG_DRWE) == 0) { + CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_1ST_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_2ND_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_3RD_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_4TH_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_5TH_KEY_VALUE); + + /* wait 100 msec */ + if (sleepFlag == CAN_SLEEP) { +/* SAE: Can't do this in the hypervisor */ +#if XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(100 * HZ / 1000); +#else + mdelay(100); +#endif + } else { + mdelay (100); + } + + count++; + if (count > 20) { + printk(MYIOC_s_ERR_FMT "Enable Diagnostic mode FAILED! (%02xh)\n", + ioc->name, diag0val); + return -EFAULT; + + } + + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); +#ifdef MPT_DEBUG + if (ioc->alt_ioc) + diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); + ddlprintk((MYIOC_s_INFO_FMT "DbGb3: diag0=%08x, diag1=%08x\n", + ioc->name, diag0val, diag1val)); +#endif + ddlprintk((MYIOC_s_INFO_FMT "Wrote magic DiagWriteEn sequence (%x)\n", + ioc->name, diag0val)); + } + + /* Set the DiagRwEn and Disable ARM bits */ + diag0val |= (MPI_DIAG_RW_ENABLE | MPI_DIAG_DISABLE_ARM); + CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); + +#ifdef MPT_DEBUG + if (ioc->alt_ioc) + diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); + + ddlprintk((MYIOC_s_INFO_FMT "DbGb3: diag0=%08x, diag1=%08x\n", + ioc->name, diag0val, diag1val)); +#endif + + /* max_idx = 1 + maximum valid buffer index + */ + max_idx = 0; + while (pCached[max_idx]) + max_idx++; + + fw_idx = 0; + FwHdr = (MpiFwHeader_t *) pCached[fw_idx]->fw; + ptru32 = (u32 *) FwHdr; + count = (FwHdr->ImageSize + 3)/4; + nextImage = FwHdr->NextImageHeaderOffset; + + /* Write the LoadStartAddress to the DiagRw Address Register + * using Programmed IO + */ + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, FwHdr->LoadStartAddress); + ddlprintk((MYIOC_s_INFO_FMT "LoadStart addr written 0x%x \n", + ioc->name, FwHdr->LoadStartAddress)); + + ddlprintk((MYIOC_s_INFO_FMT "Write FW Image: 0x%x u32's @ %p\n", + ioc->name, count, ptru32)); + left_u32s = pCached[fw_idx]->size/4; + while (count--) { + if (left_u32s == 0) { + fw_idx++; + if (fw_idx >= max_idx) { + /* FIXME + ERROR CASE + */ + ; + } + ptru32 = (u32 *) pCached[fw_idx]->fw; + left_u32s = pCached[fw_idx]->size / 4; + } + left_u32s--; + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, *ptru32); + ptru32++; + } + + /* left_u32s, fw_idx and ptru32 are all valid + */ + while (nextImage) { + ext_idx = 0; + ext_offset = nextImage; + while (ext_offset > pCached[ext_idx]->size) { + ext_idx++; + if (ext_idx >= max_idx) { + /* FIXME + ERROR CASE + */ + ; + } + ext_offset -= pCached[ext_idx]->size; + } + ptru32 = (u32 *) ((char *)pCached[ext_idx]->fw + ext_offset); + left_u32s = pCached[ext_idx]->size - ext_offset; + + if ((left_u32s * 4) >= sizeof(MpiExtImageHeader_t)) { + ExtHdr = (MpiExtImageHeader_t *) ptru32; + count = (ExtHdr->ImageSize + 3 )/4; + nextImage = ExtHdr->NextImageHeaderOffset; + load_addr = ExtHdr->LoadStartAddress; + } else { + u32 * ptmp = (u32 *)pCached[ext_idx+1]->fw; + + switch (left_u32s) { + case 5: + count = *(ptru32 + 2); + nextImage = *(ptru32 + 3); + load_addr = *(ptru32 + 4); + break; + case 4: + count = *(ptru32 + 2); + nextImage = *(ptru32 + 3); + load_addr = *ptmp; + break; + case 3: + count = *(ptru32 + 2); + nextImage = *ptmp; + load_addr = *(ptmp + 1); + break; + case 2: + count = *ptmp; + nextImage = *(ptmp + 1); + load_addr = *(ptmp + 2); + break; + + case 1: + count = *(ptmp + 1); + nextImage = *(ptmp + 2); + load_addr = *(ptmp + 3); + break; + + default: + count = 0; + nextImage = 0; + load_addr = 0; + /* FIXME + ERROR CASE + */ + ; + + } + count = (count +3)/4; + } + + ddlprintk((MYIOC_s_INFO_FMT "Write Ext Image: 0x%x u32's @ %p\n", + ioc->name, count, ptru32)); + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, load_addr); + + while (count--) { + if (left_u32s == 0) { + fw_idx++; + if (fw_idx >= max_idx) { + /* FIXME + ERROR CASE + */ + ; + } + ptru32 = (u32 *) pCached[fw_idx]->fw; + left_u32s = pCached[fw_idx]->size / 4; + } + left_u32s--; + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, *ptru32); + ptru32++; + } + } + + /* Write the IopResetVectorRegAddr */ + ddlprintk((MYIOC_s_INFO_FMT "Write IopResetVector Addr! \n", ioc->name)); + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, FwHdr->IopResetRegAddr); + + /* Write the IopResetVectorValue */ + ddlprintk((MYIOC_s_INFO_FMT "Write IopResetVector Value! \n", ioc->name)); + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, FwHdr->IopResetVectorValue); + + /* Clear the internal flash bad bit - autoincrementing register, + * so must do two writes. + */ + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, 0x3F000000); + diagRwData = CHIPREG_PIO_READ32(&ioc->pio_chip->DiagRwData); + diagRwData |= 0x4000000; + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwAddress, 0x3F000000); + CHIPREG_PIO_WRITE32(&ioc->pio_chip->DiagRwData, diagRwData); + + /* clear the RW enable and DISARM bits */ + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + diag0val &= ~(MPI_DIAG_DISABLE_ARM | MPI_DIAG_RW_ENABLE | MPI_DIAG_FLASH_BAD_SIG); + CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); + + /* Write 0xFF to reset the sequencer */ + CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF); + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * KickStart - Perform hard reset of MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * @force: Force hard reset + * @sleepFlag: Specifies whether the process can sleep + * + * This routine places MPT adapter in diagnostic mode via the + * WriteSequence register, and then performs a hard reset of adapter + * via the Diagnostic register. + * + * Inputs: sleepflag - CAN_SLEEP (non-interrupt thread) + * or NO_SLEEP (interrupt thread, use mdelay) + * force - 1 if doorbell active, board fault state + * board operational, IOC_RECOVERY or + * IOC_BRINGUP and there is an alt_ioc. + * 0 else + * + * Returns: + * 1 - hard reset, READY + * 0 - no reset due to History bit, READY + * -1 - no reset due to History bit but not READY + * OR reset but failed to come READY + * -2 - no reset, could not enter DIAG mode + * -3 - reset but bad FW bit + */ +static int +KickStart(MPT_ADAPTER *ioc, int force, int sleepFlag) +{ + int hard_reset_done = 0; + u32 ioc_state; + int cnt = 0; + + dprintk((KERN_WARNING MYNAM ": KickStarting %s!\n", ioc->name)); + if ((int)ioc->chip_type > (int)FC929) { + /* Always issue a Msg Unit Reset first. This will clear some + * SCSI bus hang conditions. + */ + SendIocReset(ioc, MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET, sleepFlag); + + if (sleepFlag == CAN_SLEEP) { +/* SAE: Can't do this in the hypervisor */ +#if XENO_KILLED + schedule_timeout(HZ); +#else + mdelay(1000); +#endif + } else { + mdelay (1000); + } + } + + hard_reset_done = mpt_diag_reset(ioc, force, sleepFlag); + if (hard_reset_done < 0) + return hard_reset_done; + + dprintk((MYIOC_s_INFO_FMT "Diagnostic reset successful!\n", + ioc->name)); + + for (cnt=0; cntname, cnt)); + return hard_reset_done; + } + if (sleepFlag == CAN_SLEEP) { +/* SAE: Can't do this in the hypervisor */ +#if XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); +#else + mdelay(10); +#endif + } else { + mdelay (10); + } + } + + printk(MYIOC_s_ERR_FMT "Failed to come READY after reset!\n", + ioc->name); + return -1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_diag_reset - Perform hard reset of the adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * @ignore: Set if to honor and clear to ignore + * the reset history bit + * @sleepflag: CAN_SLEEP if called in a non-interrupt thread, + * else set to NO_SLEEP (use mdelay instead) + * + * This routine places the adapter in diagnostic mode via the + * WriteSequence register and then performs a hard reset of adapter + * via the Diagnostic register. Adapter should be in ready state + * upon successful completion. + * + * Returns: 1 hard reset successful + * 0 no reset performed because reset history bit set + * -2 enabling diagnostic mode failed + * -3 diagnostic reset failed + */ +static int +mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag) +{ + u32 diag0val; + u32 doorbell; + int hard_reset_done = 0; + int count = 0; +#ifdef MPT_DEBUG + u32 diag1val = 0; +#endif + + /* Clear any existing interrupts */ + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + + /* Use "Diagnostic reset" method! (only thing available!) */ + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + +#ifdef MPT_DEBUG + if (ioc->alt_ioc) + diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); + dprintk((MYIOC_s_INFO_FMT "DbG1: diag0=%08x, diag1=%08x\n", + ioc->name, diag0val, diag1val)); +#endif + + /* Do the reset if we are told to ignore the reset history + * or if the reset history is 0 + */ + if (ignore || !(diag0val & MPI_DIAG_RESET_HISTORY)) { + while ((diag0val & MPI_DIAG_DRWE) == 0) { + /* Write magic sequence to WriteSequence register + * Loop until in diagnostic mode + */ + CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFF); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_1ST_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_2ND_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_3RD_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_4TH_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_5TH_KEY_VALUE); + + /* wait 100 msec */ + if (sleepFlag == CAN_SLEEP) { +/* SAE: Can't do this in the hypervisor */ +#if XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(100 * HZ / 1000); +#else + mdelay(100); +#endif + } else { + mdelay (100); + } + + count++; + if (count > 20) { + printk(MYIOC_s_ERR_FMT "Enable Diagnostic mode FAILED! (%02xh)\n", + ioc->name, diag0val); + return -2; + + } + + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + + dprintk((MYIOC_s_INFO_FMT "Wrote magic DiagWriteEn sequence (%x)\n", + ioc->name, diag0val)); + } + +#ifdef MPT_DEBUG + if (ioc->alt_ioc) + diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); + dprintk((MYIOC_s_INFO_FMT "DbG2: diag0=%08x, diag1=%08x\n", + ioc->name, diag0val, diag1val)); +#endif + /* Write the PreventIocBoot bit */ + if ((ioc->cached_fw) || (ioc->alt_ioc && ioc->alt_ioc->cached_fw)) { + diag0val |= MPI_DIAG_PREVENT_IOC_BOOT; + CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); + } + + /* + * Disable the ARM (Bug fix) + * + */ + CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val | MPI_DIAG_DISABLE_ARM); + mdelay (1); + + /* + * Now hit the reset bit in the Diagnostic register + * (THE BIG HAMMER!) (Clears DRWE bit). + */ + CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val | MPI_DIAG_RESET_ADAPTER); + hard_reset_done = 1; + dprintk((MYIOC_s_INFO_FMT "Diagnostic reset performed\n", + ioc->name)); + + /* + * Call each currently registered protocol IOC reset handler + * with pre-reset indication. + * NOTE: If we're doing _IOC_BRINGUP, there can be no + * MptResetHandlers[] registered yet. + */ + { + int ii; + int r = 0; + + for (ii=MPT_MAX_PROTOCOL_DRIVERS-1; ii; ii--) { + if (MptResetHandlers[ii]) { + dprintk((MYIOC_s_INFO_FMT "Calling IOC pre_reset handler #%d\n", + ioc->name, ii)); + r += (*(MptResetHandlers[ii]))(ioc, MPT_IOC_PRE_RESET); + if (ioc->alt_ioc) { + dprintk((MYIOC_s_INFO_FMT "Calling alt-%s pre_reset handler #%d\n", + ioc->name, ioc->alt_ioc->name, ii)); + r += (*(MptResetHandlers[ii]))(ioc->alt_ioc, MPT_IOC_PRE_RESET); + } + } + } + /* FIXME? Examine results here? */ + } + + if ((ioc->cached_fw) || (ioc->alt_ioc && ioc->alt_ioc->cached_fw)) { + /* If the DownloadBoot operation fails, the + * IOC will be left unusable. This is a fatal error + * case. _diag_reset will return < 0 + */ + for (count = 0; count < 30; count ++) { + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); +#ifdef MPT_DEBUG + if (ioc->alt_ioc) + diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); + dprintk((MYIOC_s_INFO_FMT + "DbG2b: diag0=%08x, diag1=%08x\n", + ioc->name, diag0val, diag1val)); +#endif + if (!(diag0val & MPI_DIAG_RESET_ADAPTER)) { + break; + } + + /* wait 1 sec */ + if (sleepFlag == CAN_SLEEP) { +/* SAE: Can't do this in the hypervisor */ +#if XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); +#else + mdelay(1000); +#endif + } else { + mdelay (1000); + } + } + if ((count = mpt_downloadboot(ioc, sleepFlag)) < 0) { + printk(KERN_WARNING MYNAM + ": firmware downloadboot failure (%d)!\n", count); + } + + } else { + /* Wait for FW to reload and for board + * to go to the READY state. + * Maximum wait is 60 seconds. + * If fail, no error will check again + * with calling program. + */ + for (count = 0; count < 60; count ++) { + doorbell = CHIPREG_READ32(&ioc->chip->Doorbell); + doorbell &= MPI_IOC_STATE_MASK; + + if (doorbell == MPI_IOC_STATE_READY) { + break; + } + + /* wait 1 sec */ + if (sleepFlag == CAN_SLEEP) { +/* SAE: Can't do this in the hypervisor */ +#if XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); +#else + mdelay(1000); +#endif + } else { + mdelay (1000); + } + } + } + } + + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); +#ifdef MPT_DEBUG + if (ioc->alt_ioc) + diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); + dprintk((MYIOC_s_INFO_FMT "DbG3: diag0=%08x, diag1=%08x\n", + ioc->name, diag0val, diag1val)); +#endif + + /* Clear RESET_HISTORY bit! Place board in the + * diagnostic mode to update the diag register. + */ + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + count = 0; + while ((diag0val & MPI_DIAG_DRWE) == 0) { + /* Write magic sequence to WriteSequence register + * Loop until in diagnostic mode + */ + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_1ST_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_2ND_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_3RD_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_4TH_KEY_VALUE); + CHIPREG_WRITE32(&ioc->chip->WriteSequence, MPI_WRSEQ_5TH_KEY_VALUE); + + /* wait 100 msec */ + if (sleepFlag == CAN_SLEEP) { +/* SAE: Can't do this in the hypervisor */ +#if XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(100 * HZ / 1000); +#else + mdelay(100); +#endif + } else { + mdelay (100); + } + + count++; + if (count > 20) { + printk(MYIOC_s_ERR_FMT "Enable Diagnostic mode FAILED! (%02xh)\n", + ioc->name, diag0val); + break; + } + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + } + diag0val &= ~MPI_DIAG_RESET_HISTORY; + CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val); + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + if (diag0val & MPI_DIAG_RESET_HISTORY) { + printk(MYIOC_s_WARN_FMT "ResetHistory bit failed to clear!\n", + ioc->name); + } + + /* Disable Diagnostic Mode + */ + CHIPREG_WRITE32(&ioc->chip->WriteSequence, 0xFFFFFFFF); + + /* Check FW reload status flags. + */ + diag0val = CHIPREG_READ32(&ioc->chip->Diagnostic); + if (diag0val & (MPI_DIAG_FLASH_BAD_SIG | MPI_DIAG_RESET_ADAPTER | MPI_DIAG_DISABLE_ARM)) { + printk(MYIOC_s_ERR_FMT "Diagnostic reset FAILED! (%02xh)\n", + ioc->name, diag0val); + return -3; + } + +#ifdef MPT_DEBUG + if (ioc->alt_ioc) + diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic); + dprintk((MYIOC_s_INFO_FMT "DbG4: diag0=%08x, diag1=%08x\n", + ioc->name, diag0val, diag1val)); +#endif + + /* + * Reset flag that says we've enabled event notification + */ + ioc->facts.EventState = 0; + + if (ioc->alt_ioc) + ioc->alt_ioc->facts.EventState = 0; + + return hard_reset_done; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * SendIocReset - Send IOCReset request to MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * @reset_type: reset type, expected values are + * %MPI_FUNCTION_IOC_MESSAGE_UNIT_RESET or %MPI_FUNCTION_IO_UNIT_RESET + * + * Send IOCReset request to the MPT adapter. + * + * Returns 0 for success, non-zero for failure. + */ +static int +SendIocReset(MPT_ADAPTER *ioc, u8 reset_type, int sleepFlag) +{ + int r; + u32 state; + int cntdn, count; + + dprintk((KERN_WARNING MYNAM ": %s: Sending IOC reset(0x%02x)!\n", + ioc->name, reset_type)); + CHIPREG_WRITE32(&ioc->chip->Doorbell, reset_type<name, (count+5)/HZ); + return -ETIME; + } + + if (sleepFlag == CAN_SLEEP) { +/* SAE: Can't do this in the hypervisor */ +#if XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); +#else + mdelay(1); +#endif + } else { + mdelay (1); /* 1 msec delay */ + } + } + + /* TODO! + * Cleanup all event stuff for this IOC; re-issue EventNotification + * request if needed. + */ + if (ioc->facts.Function) + ioc->facts.EventState = 0; + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * PrimeIocFifos - Initialize IOC request and reply FIFOs. + * @ioc: Pointer to MPT_ADAPTER structure + * + * This routine allocates memory for the MPT reply and request frame + * pools (if necessary), and primes the IOC reply FIFO with + * reply frames. + * + * Returns 0 for success, non-zero for failure. + */ +static int +PrimeIocFifos(MPT_ADAPTER *ioc) +{ + MPT_FRAME_HDR *mf; + unsigned long b; + unsigned long flags; + dma_addr_t aligned_mem_dma; + u8 *mem, *aligned_mem; + int i, sz; + + /* Prime reply FIFO... */ + + if (ioc->reply_frames == NULL) { + sz = (ioc->reply_sz * ioc->reply_depth) + 128; + mem = pci_alloc_consistent(ioc->pcidev, sz, &ioc->reply_alloc_dma); + if (mem == NULL) + goto out_fail; + + memset(mem, 0, sz); + ioc->alloc_total += sz; + ioc->reply_alloc = mem; + dprintk((KERN_INFO MYNAM ": %s.reply_alloc @ %p[%p], sz=%d bytes\n", + ioc->name, mem, (void *)(ulong)ioc->reply_alloc_dma, sz)); + + b = (unsigned long) mem; + b = (b + (0x80UL - 1UL)) & ~(0x80UL - 1UL); /* round up to 128-byte boundary */ + aligned_mem = (u8 *) b; + ioc->reply_frames = (MPT_FRAME_HDR *) aligned_mem; + ioc->reply_frames_dma = + (ioc->reply_alloc_dma + (aligned_mem - mem)); + + ioc->reply_frames_low_dma = (u32) (ioc->reply_frames_dma & 0xFFFFFFFF); + } + + /* Post Reply frames to FIFO + */ + aligned_mem_dma = ioc->reply_frames_dma; + dprintk((KERN_INFO MYNAM ": %s.reply_frames @ %p[%p]\n", + ioc->name, ioc->reply_frames, (void *)(ulong)aligned_mem_dma)); + + for (i = 0; i < ioc->reply_depth; i++) { + /* Write each address to the IOC! */ + CHIPREG_WRITE32(&ioc->chip->ReplyFifo, aligned_mem_dma); + aligned_mem_dma += ioc->reply_sz; + } + + + /* Request FIFO - WE manage this! */ + + if (ioc->req_frames == NULL) { + sz = (ioc->req_sz * ioc->req_depth) + 128; + /* + * Rounding UP to nearest 4-kB boundary here... + */ + sz = ((sz + 0x1000UL - 1UL) / 0x1000) * 0x1000; + + mem = pci_alloc_consistent(ioc->pcidev, sz, &ioc->req_alloc_dma); + if (mem == NULL) + goto out_fail; + + memset(mem, 0, sz); + ioc->alloc_total += sz; + ioc->req_alloc = mem; + dprintk((KERN_INFO MYNAM ": %s.req_alloc @ %p[%p], sz=%d bytes\n", + ioc->name, mem, (void *)(ulong)ioc->req_alloc_dma, sz)); + + b = (unsigned long) mem; + b = (b + (0x80UL - 1UL)) & ~(0x80UL - 1UL); /* round up to 128-byte boundary */ + aligned_mem = (u8 *) b; + ioc->req_frames = (MPT_FRAME_HDR *) aligned_mem; + ioc->req_frames_dma = + (ioc->req_alloc_dma + (aligned_mem - mem)); + + ioc->req_frames_low_dma = (u32) (ioc->req_frames_dma & 0xFFFFFFFF); + + if (sizeof(dma_addr_t) == sizeof(u64)) { + /* Check: upper 32-bits of the request and reply frame + * physical addresses must be the same. + */ + if (((u64)ioc->req_frames_dma >> 32) != ((u64)ioc->reply_frames_dma >> 32)){ + goto out_fail; + } + } + +#if defined(CONFIG_MTRR) && 0 + /* + * Enable Write Combining MTRR for IOC's memory region. + * (at least as much as we can; "size and base must be + * multiples of 4 kiB" + */ + ioc->mtrr_reg = mtrr_add(ioc->req_alloc_dma, + sz, + MTRR_TYPE_WRCOMB, 1); + dprintk((MYIOC_s_INFO_FMT "MTRR region registered (base:size=%08x:%x)\n", + ioc->name, ioc->req_alloc_dma, sz)); +#endif + } + + /* Initialize Request frames linked list + */ + aligned_mem_dma = ioc->req_frames_dma; + aligned_mem = (u8 *) ioc->req_frames; + dprintk((KERN_INFO MYNAM ": %s.req_frames @ %p[%p]\n", + ioc->name, aligned_mem, (void *)(ulong)aligned_mem_dma)); + + spin_lock_irqsave(&ioc->FreeQlock, flags); + Q_INIT(&ioc->FreeQ, MPT_FRAME_HDR); + for (i = 0; i < ioc->req_depth; i++) { + mf = (MPT_FRAME_HDR *) aligned_mem; + + /* Queue REQUESTs *internally*! */ + Q_ADD_TAIL(&ioc->FreeQ.head, &mf->u.frame.linkage, MPT_FRAME_HDR); + aligned_mem += ioc->req_sz; + } + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + + + if (ioc->sense_buf_pool == NULL) { + sz = (ioc->req_depth * MPT_SENSE_BUFFER_ALLOC); + ioc->sense_buf_pool = + pci_alloc_consistent(ioc->pcidev, sz, &ioc->sense_buf_pool_dma); + if (ioc->sense_buf_pool == NULL) + goto out_fail; + + ioc->sense_buf_low_dma = (u32) (ioc->sense_buf_pool_dma & 0xFFFFFFFF); + ioc->alloc_total += sz; + } + + return 0; + +out_fail: + if (ioc->reply_alloc != NULL) { + sz = (ioc->reply_sz * ioc->reply_depth) + 128; + pci_free_consistent(ioc->pcidev, + sz, + ioc->reply_alloc, ioc->reply_alloc_dma); + ioc->reply_frames = NULL; + ioc->reply_alloc = NULL; + ioc->alloc_total -= sz; + } + if (ioc->req_alloc != NULL) { + sz = (ioc->req_sz * ioc->req_depth) + 128; + /* + * Rounding UP to nearest 4-kB boundary here... + */ + sz = ((sz + 0x1000UL - 1UL) / 0x1000) * 0x1000; + pci_free_consistent(ioc->pcidev, + sz, + ioc->req_alloc, ioc->req_alloc_dma); +#if defined(CONFIG_MTRR) && 0 + if (ioc->mtrr_reg > 0) { + mtrr_del(ioc->mtrr_reg, 0, 0); + dprintk((MYIOC_s_INFO_FMT "MTRR region de-registered\n", + ioc->name)); + } +#endif + ioc->req_frames = NULL; + ioc->req_alloc = NULL; + ioc->alloc_total -= sz; + } + if (ioc->sense_buf_pool != NULL) { + sz = (ioc->req_depth * MPT_SENSE_BUFFER_ALLOC); + pci_free_consistent(ioc->pcidev, + sz, + ioc->sense_buf_pool, ioc->sense_buf_pool_dma); + ioc->sense_buf_pool = NULL; + } + return -1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_handshake_req_reply_wait - Send MPT request to and receive reply from + * IOC via doorbell handshake method. + * @ioc: Pointer to MPT_ADAPTER structure + * @reqBytes: Size of the request in bytes + * @req: Pointer to MPT request frame + * @replyBytes: Expected size of the reply in bytes + * @u16reply: Pointer to area where reply should be written + * @maxwait: Max wait time for a reply (in seconds) + * @sleepFlag: Specifies whether the process can sleep + * + * NOTES: It is the callers responsibility to byte-swap fields in the + * request which are greater than 1 byte in size. It is also the + * callers responsibility to byte-swap response fields which are + * greater than 1 byte in size. + * + * Returns 0 for success, non-zero for failure. + */ +int +mpt_handshake_req_reply_wait(MPT_ADAPTER *ioc, int reqBytes, u32 *req, + int replyBytes, u16 *u16reply, int maxwait, int sleepFlag) +{ + MPIDefaultReply_t *mptReply; + int failcnt = 0; + int t; + + /* + * Get ready to cache a handshake reply + */ + ioc->hs_reply_idx = 0; + mptReply = (MPIDefaultReply_t *) ioc->hs_reply; + mptReply->MsgLength = 0; + + /* + * Make sure there are no doorbells (WRITE 0 to IntStatus reg), + * then tell IOC that we want to handshake a request of N words. + * (WRITE u32val to Doorbell reg). + */ + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + CHIPREG_WRITE32(&ioc->chip->Doorbell, + ((MPI_FUNCTION_HANDSHAKE<name, t, failcnt ? " - MISSING DOORBELL HANDSHAKE!" : "")); + + /* Read doorbell and check for active bit */ + if (!(CHIPREG_READ32(&ioc->chip->Doorbell) & MPI_DOORBELL_ACTIVE)) + return -1; + + /* + * Clear doorbell int (WRITE 0 to IntStatus reg), + * then wait for IOC to ACKnowledge that it's ready for + * our handshake request. + */ + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + if (!failcnt && (t = WaitForDoorbellAck(ioc, 4, sleepFlag)) < 0) + failcnt++; + + if (!failcnt) { + int ii; + u8 *req_as_bytes = (u8 *) req; + + /* + * Stuff request words via doorbell handshake, + * with ACK from IOC for each. + */ + for (ii = 0; !failcnt && ii < reqBytes/4; ii++) { + u32 word = ((req_as_bytes[(ii*4) + 0] << 0) | + (req_as_bytes[(ii*4) + 1] << 8) | + (req_as_bytes[(ii*4) + 2] << 16) | + (req_as_bytes[(ii*4) + 3] << 24)); + + CHIPREG_WRITE32(&ioc->chip->Doorbell, word); + if ((t = WaitForDoorbellAck(ioc, 4, sleepFlag)) < 0) + failcnt++; + } + + dmfprintk((KERN_INFO MYNAM ": Handshake request frame (@%p) header\n", req)); + DBG_DUMP_REQUEST_FRAME_HDR(req) + + dhsprintk((MYIOC_s_INFO_FMT "HandShake request post done, WaitCnt=%d%s\n", + ioc->name, t, failcnt ? " - MISSING DOORBELL ACK!" : "")); + + /* + * Wait for completion of doorbell handshake reply from the IOC + */ + if (!failcnt && (t = WaitForDoorbellReply(ioc, maxwait, sleepFlag)) < 0) + failcnt++; + + /* + * Copy out the cached reply... + */ + for (ii=0; ii < MIN(replyBytes/2,mptReply->MsgLength*2); ii++) + u16reply[ii] = ioc->hs_reply[ii]; + } else { + return -99; + } + + return -failcnt; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * WaitForDoorbellAck - Wait for IOC to clear the IOP_DOORBELL_STATUS bit + * in it's IntStatus register. + * @ioc: Pointer to MPT_ADAPTER structure + * @howlong: How long to wait (in seconds) + * @sleepFlag: Specifies whether the process can sleep + * + * This routine waits (up to ~2 seconds max) for IOC doorbell + * handshake ACKnowledge. + * + * Returns a negative value on failure, else wait loop count. + */ +static int +WaitForDoorbellAck(MPT_ADAPTER *ioc, int howlong, int sleepFlag) +{ + int cntdn; + int count = 0; + u32 intstat; + + cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * howlong; + + if (sleepFlag == CAN_SLEEP) { + while (--cntdn) { + intstat = CHIPREG_READ32(&ioc->chip->IntStatus); + if (! (intstat & MPI_HIS_IOP_DOORBELL_STATUS)) + break; +/* SAE: Can't do this in the hypervisor */ +#if XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); +#else + mdelay(1); +#endif + count++; + } + } else { + while (--cntdn) { + intstat = CHIPREG_READ32(&ioc->chip->IntStatus); + if (! (intstat & MPI_HIS_IOP_DOORBELL_STATUS)) + break; + mdelay (1); + count++; + } + } + + if (cntdn) { + dhsprintk((MYIOC_s_INFO_FMT "WaitForDoorbell ACK (cnt=%d)\n", + ioc->name, count)); + return count; + } + + printk(MYIOC_s_ERR_FMT "Doorbell ACK timeout(%d)!\n", + ioc->name, (count+5)/HZ); + return -1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * WaitForDoorbellInt - Wait for IOC to set the HIS_DOORBELL_INTERRUPT bit + * in it's IntStatus register. + * @ioc: Pointer to MPT_ADAPTER structure + * @howlong: How long to wait (in seconds) + * @sleepFlag: Specifies whether the process can sleep + * + * This routine waits (up to ~2 seconds max) for IOC doorbell interrupt. + * + * Returns a negative value on failure, else wait loop count. + */ +static int +WaitForDoorbellInt(MPT_ADAPTER *ioc, int howlong, int sleepFlag) +{ + int cntdn; + int count = 0; + u32 intstat; + + cntdn = ((sleepFlag == CAN_SLEEP) ? HZ : 1000) * howlong; + if (sleepFlag == CAN_SLEEP) { + while (--cntdn) { + intstat = CHIPREG_READ32(&ioc->chip->IntStatus); + if (intstat & MPI_HIS_DOORBELL_INTERRUPT) + break; +/* SAE: Can't do this in the hypervisor */ +#if XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); +#else + mdelay(1); +#endif + count++; + } + } else { + while (--cntdn) { + intstat = CHIPREG_READ32(&ioc->chip->IntStatus); + if (intstat & MPI_HIS_DOORBELL_INTERRUPT) + break; + mdelay(1); + count++; + } + } + + if (cntdn) { + dhsprintk((MYIOC_s_INFO_FMT "WaitForDoorbell INT (cnt=%d)\n", + ioc->name, count)); + return count; + } + + printk(MYIOC_s_ERR_FMT "Doorbell INT timeout(%d)!\n", + ioc->name, (count+5)/HZ); + return -1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * WaitForDoorbellReply - Wait for and capture a IOC handshake reply. + * @ioc: Pointer to MPT_ADAPTER structure + * @howlong: How long to wait (in seconds) + * @sleepFlag: Specifies whether the process can sleep + * + * This routine polls the IOC for a handshake reply, 16 bits at a time. + * Reply is cached to IOC private area large enough to hold a maximum + * of 128 bytes of reply data. + * + * Returns a negative value on failure, else size of reply in WORDS. + */ +static int +WaitForDoorbellReply(MPT_ADAPTER *ioc, int howlong, int sleepFlag) +{ + int u16cnt = 0; + int failcnt = 0; + int t; + u16 *hs_reply = ioc->hs_reply; + volatile MPIDefaultReply_t *mptReply = (MPIDefaultReply_t *) ioc->hs_reply; + u16 hword; + + hs_reply[0] = hs_reply[1] = hs_reply[7] = 0; + + /* + * Get first two u16's so we can look at IOC's intended reply MsgLength + */ + u16cnt=0; + if ((t = WaitForDoorbellInt(ioc, howlong, sleepFlag)) < 0) { + failcnt++; + } else { + hs_reply[u16cnt++] = le16_to_cpu(CHIPREG_READ32(&ioc->chip->Doorbell) & 0x0000FFFF); + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + if ((t = WaitForDoorbellInt(ioc, 2, sleepFlag)) < 0) + failcnt++; + else { + hs_reply[u16cnt++] = le16_to_cpu(CHIPREG_READ32(&ioc->chip->Doorbell) & 0x0000FFFF); + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + } + } + + dhsprintk((MYIOC_s_INFO_FMT "First handshake reply word=%08x%s\n", + ioc->name, le32_to_cpu(*(u32 *)hs_reply), + failcnt ? " - MISSING DOORBELL HANDSHAKE!" : "")); + + /* + * If no error (and IOC said MsgLength is > 0), piece together + * reply 16 bits at a time. + */ + for (u16cnt=2; !failcnt && u16cnt < (2 * mptReply->MsgLength); u16cnt++) { + if ((t = WaitForDoorbellInt(ioc, 2, sleepFlag)) < 0) + failcnt++; + hword = le16_to_cpu(CHIPREG_READ32(&ioc->chip->Doorbell) & 0x0000FFFF); + /* don't overflow our IOC hs_reply[] buffer! */ + if (u16cnt < sizeof(ioc->hs_reply) / sizeof(ioc->hs_reply[0])) + hs_reply[u16cnt] = hword; + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + } + + if (!failcnt && (t = WaitForDoorbellInt(ioc, 2, sleepFlag)) < 0) + failcnt++; + CHIPREG_WRITE32(&ioc->chip->IntStatus, 0); + + if (failcnt) { + printk(MYIOC_s_ERR_FMT "Handshake reply failure!\n", + ioc->name); + return -failcnt; + } +#if 0 + else if (u16cnt != (2 * mptReply->MsgLength)) { + return -101; + } + else if ((mptReply->IOCStatus & MPI_IOCSTATUS_MASK) != MPI_IOCSTATUS_SUCCESS) { + return -102; + } +#endif + + dmfprintk((MYIOC_s_INFO_FMT "Got Handshake reply:\n", ioc->name)); + DBG_DUMP_REPLY_FRAME(mptReply) + + dhsprintk((MYIOC_s_INFO_FMT "WaitForDoorbell REPLY (sz=%d)\n", + ioc->name, u16cnt/2)); + return u16cnt/2; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * GetLanConfigPages - Fetch LANConfig pages. + * @ioc: Pointer to MPT_ADAPTER structure + * + * Return: 0 for success + * -ENOMEM if no memory available + * -EPERM if not allowed due to ISR context + * -EAGAIN if no msg frames currently available + * -EFAULT for non-successful reply or no reply (timeout) + */ +static int +GetLanConfigPages(MPT_ADAPTER *ioc) +{ + ConfigPageHeader_t hdr; + CONFIGPARMS cfg; + LANPage0_t *ppage0_alloc; + dma_addr_t page0_dma; + LANPage1_t *ppage1_alloc; + dma_addr_t page1_dma; + int rc = 0; + int data_sz; + int copy_sz; + + /* Get LAN Page 0 header */ + hdr.PageVersion = 0; + hdr.PageLength = 0; + hdr.PageNumber = 0; + hdr.PageType = MPI_CONFIG_PAGETYPE_LAN; + cfg.hdr = &hdr; + cfg.physAddr = -1; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; + cfg.pageAddr = 0; + cfg.timeout = 0; + + if ((rc = mpt_config(ioc, &cfg)) != 0) + return rc; + + if (hdr.PageLength > 0) { + data_sz = hdr.PageLength * 4; + ppage0_alloc = (LANPage0_t *) pci_alloc_consistent(ioc->pcidev, data_sz, &page0_dma); + rc = -ENOMEM; + if (ppage0_alloc) { + memset((u8 *)ppage0_alloc, 0, data_sz); + cfg.physAddr = page0_dma; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + if ((rc = mpt_config(ioc, &cfg)) == 0) { + /* save the data */ + copy_sz = MIN(sizeof(LANPage0_t), data_sz); + memcpy(&ioc->lan_cnfg_page0, ppage0_alloc, copy_sz); + + } + + pci_free_consistent(ioc->pcidev, data_sz, (u8 *) ppage0_alloc, page0_dma); + + /* FIXME! + * Normalize endianness of structure data, + * by byte-swapping all > 1 byte fields! + */ + + } + + if (rc) + return rc; + } + + /* Get LAN Page 1 header */ + hdr.PageVersion = 0; + hdr.PageLength = 0; + hdr.PageNumber = 1; + hdr.PageType = MPI_CONFIG_PAGETYPE_LAN; + cfg.hdr = &hdr; + cfg.physAddr = -1; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; + cfg.pageAddr = 0; + + if ((rc = mpt_config(ioc, &cfg)) != 0) + return rc; + + if (hdr.PageLength == 0) + return 0; + + data_sz = hdr.PageLength * 4; + rc = -ENOMEM; + ppage1_alloc = (LANPage1_t *) pci_alloc_consistent(ioc->pcidev, data_sz, &page1_dma); + if (ppage1_alloc) { + memset((u8 *)ppage1_alloc, 0, data_sz); + cfg.physAddr = page1_dma; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + if ((rc = mpt_config(ioc, &cfg)) == 0) { + /* save the data */ + copy_sz = MIN(sizeof(LANPage1_t), data_sz); + memcpy(&ioc->lan_cnfg_page1, ppage1_alloc, copy_sz); + } + + pci_free_consistent(ioc->pcidev, data_sz, (u8 *) ppage1_alloc, page1_dma); + + /* FIXME! + * Normalize endianness of structure data, + * by byte-swapping all > 1 byte fields! + */ + + } + + return rc; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * GetFcPortPage0 - Fetch FCPort config Page0. + * @ioc: Pointer to MPT_ADAPTER structure + * @portnum: IOC Port number + * + * Return: 0 for success + * -ENOMEM if no memory available + * -EPERM if not allowed due to ISR context + * -EAGAIN if no msg frames currently available + * -EFAULT for non-successful reply or no reply (timeout) + */ +static int +GetFcPortPage0(MPT_ADAPTER *ioc, int portnum) +{ + ConfigPageHeader_t hdr; + CONFIGPARMS cfg; + FCPortPage0_t *ppage0_alloc; + FCPortPage0_t *pp0dest; + dma_addr_t page0_dma; + int data_sz; + int copy_sz; + int rc; + + /* Get FCPort Page 0 header */ + hdr.PageVersion = 0; + hdr.PageLength = 0; + hdr.PageNumber = 0; + hdr.PageType = MPI_CONFIG_PAGETYPE_FC_PORT; + cfg.hdr = &hdr; + cfg.physAddr = -1; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; + cfg.pageAddr = portnum; + cfg.timeout = 0; + + if ((rc = mpt_config(ioc, &cfg)) != 0) + return rc; + + if (hdr.PageLength == 0) + return 0; + + data_sz = hdr.PageLength * 4; + rc = -ENOMEM; + ppage0_alloc = (FCPortPage0_t *) pci_alloc_consistent(ioc->pcidev, data_sz, &page0_dma); + if (ppage0_alloc) { + memset((u8 *)ppage0_alloc, 0, data_sz); + cfg.physAddr = page0_dma; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + if ((rc = mpt_config(ioc, &cfg)) == 0) { + /* save the data */ + pp0dest = &ioc->fc_port_page0[portnum]; + copy_sz = MIN(sizeof(FCPortPage0_t), data_sz); + memcpy(pp0dest, ppage0_alloc, copy_sz); + + /* + * Normalize endianness of structure data, + * by byte-swapping all > 1 byte fields! + */ + pp0dest->Flags = le32_to_cpu(pp0dest->Flags); + pp0dest->PortIdentifier = le32_to_cpu(pp0dest->PortIdentifier); + pp0dest->WWNN.Low = le32_to_cpu(pp0dest->WWNN.Low); + pp0dest->WWNN.High = le32_to_cpu(pp0dest->WWNN.High); + pp0dest->WWPN.Low = le32_to_cpu(pp0dest->WWPN.Low); + pp0dest->WWPN.High = le32_to_cpu(pp0dest->WWPN.High); + pp0dest->SupportedServiceClass = le32_to_cpu(pp0dest->SupportedServiceClass); + pp0dest->SupportedSpeeds = le32_to_cpu(pp0dest->SupportedSpeeds); + pp0dest->CurrentSpeed = le32_to_cpu(pp0dest->CurrentSpeed); + pp0dest->MaxFrameSize = le32_to_cpu(pp0dest->MaxFrameSize); + pp0dest->FabricWWNN.Low = le32_to_cpu(pp0dest->FabricWWNN.Low); + pp0dest->FabricWWNN.High = le32_to_cpu(pp0dest->FabricWWNN.High); + pp0dest->FabricWWPN.Low = le32_to_cpu(pp0dest->FabricWWPN.Low); + pp0dest->FabricWWPN.High = le32_to_cpu(pp0dest->FabricWWPN.High); + pp0dest->DiscoveredPortsCount = le32_to_cpu(pp0dest->DiscoveredPortsCount); + pp0dest->MaxInitiators = le32_to_cpu(pp0dest->MaxInitiators); + + } + + pci_free_consistent(ioc->pcidev, data_sz, (u8 *) ppage0_alloc, page0_dma); + } + + return rc; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * GetIoUnitPage2 - Retrieve BIOS version and boot order information. + * @ioc: Pointer to MPT_ADAPTER structure + * + * Returns: 0 for success + * -ENOMEM if no memory available + * -EPERM if not allowed due to ISR context + * -EAGAIN if no msg frames currently available + * -EFAULT for non-successful reply or no reply (timeout) + */ +static int +GetIoUnitPage2(MPT_ADAPTER *ioc) +{ + ConfigPageHeader_t hdr; + CONFIGPARMS cfg; + IOUnitPage2_t *ppage_alloc; + dma_addr_t page_dma; + int data_sz; + int rc; + + /* Get the page header */ + hdr.PageVersion = 0; + hdr.PageLength = 0; + hdr.PageNumber = 2; + hdr.PageType = MPI_CONFIG_PAGETYPE_IO_UNIT; + cfg.hdr = &hdr; + cfg.physAddr = -1; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; + cfg.pageAddr = 0; + cfg.timeout = 0; + + if ((rc = mpt_config(ioc, &cfg)) != 0) + return rc; + + if (hdr.PageLength == 0) + return 0; + + /* Read the config page */ + data_sz = hdr.PageLength * 4; + rc = -ENOMEM; + ppage_alloc = (IOUnitPage2_t *) pci_alloc_consistent(ioc->pcidev, data_sz, &page_dma); + if (ppage_alloc) { + memset((u8 *)ppage_alloc, 0, data_sz); + cfg.physAddr = page_dma; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + + /* If Good, save data */ + if ((rc = mpt_config(ioc, &cfg)) == 0) + ioc->biosVersion = le32_to_cpu(ppage_alloc->BiosVersion); + + pci_free_consistent(ioc->pcidev, data_sz, (u8 *) ppage_alloc, page_dma); + } + + return rc; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* mpt_GetScsiPortSettings - read SCSI Port Page 0 and 2 + * @ioc: Pointer to a Adapter Strucutre + * @portnum: IOC port number + * + * Return: -EFAULT if read of config page header fails + * or if no nvram + * If read of SCSI Port Page 0 fails, + * NVRAM = MPT_HOST_NVRAM_INVALID (0xFFFFFFFF) + * Adapter settings: async, narrow + * Return 1 + * If read of SCSI Port Page 2 fails, + * Adapter settings valid + * NVRAM = MPT_HOST_NVRAM_INVALID (0xFFFFFFFF) + * Return 1 + * Else + * Both valid + * Return 0 + * CHECK - what type of locking mechanisms should be used???? + */ +static int +mpt_GetScsiPortSettings(MPT_ADAPTER *ioc, int portnum) +{ + u8 *pbuf = NULL; + dma_addr_t buf_dma; + CONFIGPARMS cfg; + ConfigPageHeader_t header; + int ii; + int data, rc = 0; + + /* Allocate memory + */ + if (!ioc->spi_data.nvram) { + int sz; + u8 *mem; + sz = MPT_MAX_SCSI_DEVICES * sizeof(int); + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) + return -EFAULT; + + ioc->spi_data.nvram = (int *) mem; + + dprintk((MYIOC_s_INFO_FMT "SCSI device NVRAM settings @ %p, sz=%d\n", + ioc->name, ioc->spi_data.nvram, sz)); + } + + /* Invalidate NVRAM information + */ + for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) { + ioc->spi_data.nvram[ii] = MPT_HOST_NVRAM_INVALID; + } + + /* Read SPP0 header, allocate memory, then read page. + */ + header.PageVersion = 0; + header.PageLength = 0; + header.PageNumber = 0; + header.PageType = MPI_CONFIG_PAGETYPE_SCSI_PORT; + cfg.hdr = &header; + cfg.physAddr = -1; + cfg.pageAddr = portnum; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; + cfg.timeout = 0; /* use default */ + if (mpt_config(ioc, &cfg) != 0) + return -EFAULT; + + if (header.PageLength > 0) { + pbuf = pci_alloc_consistent(ioc->pcidev, header.PageLength * 4, &buf_dma); + if (pbuf) { + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + cfg.physAddr = buf_dma; + if (mpt_config(ioc, &cfg) != 0) { + ioc->spi_data.maxBusWidth = MPT_NARROW; + ioc->spi_data.maxSyncOffset = 0; + ioc->spi_data.minSyncFactor = MPT_ASYNC; + ioc->spi_data.busType = MPT_HOST_BUS_UNKNOWN; + rc = 1; + } else { + /* Save the Port Page 0 data + */ + SCSIPortPage0_t *pPP0 = (SCSIPortPage0_t *) pbuf; + pPP0->Capabilities = le32_to_cpu(pPP0->Capabilities); + pPP0->PhysicalInterface = le32_to_cpu(pPP0->PhysicalInterface); + + ioc->spi_data.maxBusWidth = pPP0->Capabilities & MPI_SCSIPORTPAGE0_CAP_WIDE ? 1 : 0; + data = pPP0->Capabilities & MPI_SCSIPORTPAGE0_CAP_MAX_SYNC_OFFSET_MASK; + if (data) { + ioc->spi_data.maxSyncOffset = (u8) (data >> 16); + data = pPP0->Capabilities & MPI_SCSIPORTPAGE0_CAP_MIN_SYNC_PERIOD_MASK; + ioc->spi_data.minSyncFactor = (u8) (data >> 8); + } else { + ioc->spi_data.maxSyncOffset = 0; + ioc->spi_data.minSyncFactor = MPT_ASYNC; + } + + ioc->spi_data.busType = pPP0->PhysicalInterface & MPI_SCSIPORTPAGE0_PHY_SIGNAL_TYPE_MASK; + + /* Update the minSyncFactor based on bus type. + */ + if ((ioc->spi_data.busType == MPI_SCSIPORTPAGE0_PHY_SIGNAL_HVD) || + (ioc->spi_data.busType == MPI_SCSIPORTPAGE0_PHY_SIGNAL_SE)) { + + if (ioc->spi_data.minSyncFactor < MPT_ULTRA) + ioc->spi_data.minSyncFactor = MPT_ULTRA; + } + } + if (pbuf) { + pci_free_consistent(ioc->pcidev, header.PageLength * 4, pbuf, buf_dma); + pbuf = NULL; + } + } + } + + /* SCSI Port Page 2 - Read the header then the page. + */ + header.PageVersion = 0; + header.PageLength = 0; + header.PageNumber = 2; + header.PageType = MPI_CONFIG_PAGETYPE_SCSI_PORT; + cfg.hdr = &header; + cfg.physAddr = -1; + cfg.pageAddr = portnum; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; + if (mpt_config(ioc, &cfg) != 0) + return -EFAULT; + + if (header.PageLength > 0) { + /* Allocate memory and read SCSI Port Page 2 + */ + pbuf = pci_alloc_consistent(ioc->pcidev, header.PageLength * 4, &buf_dma); + if (pbuf) { + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_NVRAM; + cfg.physAddr = buf_dma; + if (mpt_config(ioc, &cfg) != 0) { + /* Nvram data is left with INVALID mark + */ + rc = 1; + } else { + SCSIPortPage2_t *pPP2 = (SCSIPortPage2_t *) pbuf; + MpiDeviceInfo_t *pdevice = NULL; + + /* Save the Port Page 2 data + * (reformat into a 32bit quantity) + */ + for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) { + pdevice = &pPP2->DeviceSettings[ii]; + data = (le16_to_cpu(pdevice->DeviceFlags) << 16) | + (pdevice->SyncFactor << 8) | pdevice->Timeout; + ioc->spi_data.nvram[ii] = data; + } + } + + pci_free_consistent(ioc->pcidev, header.PageLength * 4, pbuf, buf_dma); + pbuf = NULL; + } + } + + /* Update Adapter limits with those from NVRAM + * Comment: Don't need to do this. Target performance + * parameters will never exceed the adapters limits. + */ + + return rc; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* mpt_readScsiDevicePageHeaders - save version and length of SDP1 + * @ioc: Pointer to a Adapter Strucutre + * @portnum: IOC port number + * + * Return: -EFAULT if read of config page header fails + * or 0 if success. + */ +static int +mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum) +{ + CONFIGPARMS cfg; + ConfigPageHeader_t header; + + /* Read the SCSI Device Page 1 header + */ + header.PageVersion = 0; + header.PageLength = 0; + header.PageNumber = 1; + header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE; + cfg.hdr = &header; + cfg.physAddr = -1; + cfg.pageAddr = portnum; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; + cfg.timeout = 0; + if (mpt_config(ioc, &cfg) != 0) + return -EFAULT; + + ioc->spi_data.sdp1version = cfg.hdr->PageVersion; + ioc->spi_data.sdp1length = cfg.hdr->PageLength; + + header.PageVersion = 0; + header.PageLength = 0; + header.PageNumber = 0; + header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE; + if (mpt_config(ioc, &cfg) != 0) + return -EFAULT; + + ioc->spi_data.sdp0version = cfg.hdr->PageVersion; + ioc->spi_data.sdp0length = cfg.hdr->PageLength; + + dcprintk((MYIOC_s_INFO_FMT "Headers: 0: version %d length %d\n", + ioc->name, ioc->spi_data.sdp0version, ioc->spi_data.sdp0length)); + + dcprintk((MYIOC_s_INFO_FMT "Headers: 1: version %d length %d\n", + ioc->name, ioc->spi_data.sdp1version, ioc->spi_data.sdp1length)); + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_findImVolumes - Identify IDs of hidden disks and RAID Volumes + * @ioc: Pointer to a Adapter Strucutre + * @portnum: IOC port number + * + * Return: + * 0 on success + * -EFAULT if read of config page header fails or data pointer not NULL + * -ENOMEM if pci_alloc failed + */ +static int +mpt_findImVolumes(MPT_ADAPTER *ioc) +{ + IOCPage2_t *pIoc2 = NULL; + ConfigPageIoc2RaidVol_t *pIocRv = NULL; + dma_addr_t ioc2_dma; + CONFIGPARMS cfg; + ConfigPageHeader_t header; + int jj; + int rc = 0; + int iocpage2sz; + u8 nVols, nPhys; + u8 vid, vbus, vioc; + + if (ioc->spi_data.pIocPg3) + return -EFAULT; + + /* Read IOCP2 header then the page. + */ + header.PageVersion = 0; + header.PageLength = 0; + header.PageNumber = 2; + header.PageType = MPI_CONFIG_PAGETYPE_IOC; + cfg.hdr = &header; + cfg.physAddr = -1; + cfg.pageAddr = 0; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; + cfg.timeout = 0; + if (mpt_config(ioc, &cfg) != 0) + return -EFAULT; + + if (header.PageLength == 0) + return -EFAULT; + + iocpage2sz = header.PageLength * 4; + pIoc2 = pci_alloc_consistent(ioc->pcidev, iocpage2sz, &ioc2_dma); + if (!pIoc2) + return -ENOMEM; + + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + cfg.physAddr = ioc2_dma; + if (mpt_config(ioc, &cfg) != 0) + goto done_and_free; + + /* Identify RAID Volume Id's */ + nVols = pIoc2->NumActiveVolumes; + if ( nVols == 0) { + /* No RAID Volumes. Done. + */ + } else { + /* At least 1 RAID Volume + */ + pIocRv = pIoc2->RaidVolume; + ioc->spi_data.isRaid = 0; + for (jj = 0; jj < nVols; jj++, pIocRv++) { + vid = pIocRv->VolumeID; + vbus = pIocRv->VolumeBus; + vioc = pIocRv->VolumeIOC; + + /* find the match + */ + if (vbus == 0) { + ioc->spi_data.isRaid |= (1 << vid); + } else { + /* Error! Always bus 0 + */ + } + } + } + + /* Identify Hidden Physical Disk Id's */ + nPhys = pIoc2->NumActivePhysDisks; + if (nPhys == 0) { + /* No physical disks. Done. + */ + } else { + mpt_read_ioc_pg_3(ioc); + } + +done_and_free: + if (pIoc2) { + pci_free_consistent(ioc->pcidev, iocpage2sz, pIoc2, ioc2_dma); + pIoc2 = NULL; + } + + return rc; +} + +int +mpt_read_ioc_pg_3(MPT_ADAPTER *ioc) +{ + IOCPage3_t *pIoc3 = NULL; + u8 *mem; + CONFIGPARMS cfg; + ConfigPageHeader_t header; + dma_addr_t ioc3_dma; + int iocpage3sz = 0; + + /* Free the old page + */ + if (ioc->spi_data.pIocPg3) { + kfree(ioc->spi_data.pIocPg3); + ioc->spi_data.pIocPg3 = NULL; + } + + /* There is at least one physical disk. + * Read and save IOC Page 3 + */ + header.PageVersion = 0; + header.PageLength = 0; + header.PageNumber = 3; + header.PageType = MPI_CONFIG_PAGETYPE_IOC; + cfg.hdr = &header; + cfg.physAddr = -1; + cfg.pageAddr = 0; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; + cfg.timeout = 0; + if (mpt_config(ioc, &cfg) != 0) + return 0; + + if (header.PageLength == 0) + return 0; + + /* Read Header good, alloc memory + */ + iocpage3sz = header.PageLength * 4; + pIoc3 = pci_alloc_consistent(ioc->pcidev, iocpage3sz, &ioc3_dma); + if (!pIoc3) + return 0; + + /* Read the Page and save the data + * into malloc'd memory. + */ + cfg.physAddr = ioc3_dma; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + if (mpt_config(ioc, &cfg) == 0) { + mem = kmalloc(iocpage3sz, GFP_ATOMIC); + if (mem) { + memcpy(mem, (u8 *)pIoc3, iocpage3sz); + ioc->spi_data.pIocPg3 = (IOCPage3_t *) mem; + } + } + + if (pIoc3) { + pci_free_consistent(ioc->pcidev, iocpage3sz, pIoc3, ioc3_dma); + pIoc3 = NULL; + } + + return 0; +} + +static void +mpt_read_ioc_pg_1(MPT_ADAPTER *ioc) +{ + IOCPage1_t *pIoc1 = NULL; + CONFIGPARMS cfg; + ConfigPageHeader_t header; + dma_addr_t ioc1_dma; + int iocpage1sz = 0; + u32 tmp; + + /* Check the Coalescing Timeout in IOC Page 1 + */ + header.PageVersion = 0; + header.PageLength = 0; + header.PageNumber = 1; + header.PageType = MPI_CONFIG_PAGETYPE_IOC; + cfg.hdr = &header; + cfg.physAddr = -1; + cfg.pageAddr = 0; + cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER; + cfg.dir = 0; + cfg.timeout = 0; + if (mpt_config(ioc, &cfg) != 0) + return; + + if (header.PageLength == 0) + return; + + /* Read Header good, alloc memory + */ + iocpage1sz = header.PageLength * 4; + pIoc1 = pci_alloc_consistent(ioc->pcidev, iocpage1sz, &ioc1_dma); + if (!pIoc1) + return; + + /* Read the Page and check coalescing timeout + */ + cfg.physAddr = ioc1_dma; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + if (mpt_config(ioc, &cfg) == 0) { + + tmp = le32_to_cpu(pIoc1->Flags) & MPI_IOCPAGE1_REPLY_COALESCING; + if (tmp == MPI_IOCPAGE1_REPLY_COALESCING) { + tmp = le32_to_cpu(pIoc1->CoalescingTimeout); + + dprintk((MYIOC_s_INFO_FMT "Coalescing Enabled Timeout = %d\n", + ioc->name, tmp)); + + if (tmp > MPT_COALESCING_TIMEOUT) { + pIoc1->CoalescingTimeout = cpu_to_le32(MPT_COALESCING_TIMEOUT); + + /* Write NVRAM and current + */ + cfg.dir = 1; + cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; + if (mpt_config(ioc, &cfg) == 0) { + dprintk((MYIOC_s_INFO_FMT "Reset Current Coalescing Timeout to = %d\n", + ioc->name, MPT_COALESCING_TIMEOUT)); + + cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM; + if (mpt_config(ioc, &cfg) == 0) { + dprintk((MYIOC_s_INFO_FMT "Reset NVRAM Coalescing Timeout to = %d\n", + ioc->name, MPT_COALESCING_TIMEOUT)); + } else { + dprintk((MYIOC_s_INFO_FMT "Reset NVRAM Coalescing Timeout Failed\n", + ioc->name)); + } + + } else { + dprintk((MYIOC_s_WARN_FMT "Reset of Current Coalescing Timeout Failed!\n", + ioc->name)); + } + } + + } else { + dprintk((MYIOC_s_WARN_FMT "Coalescing Disabled\n", ioc->name)); + } + } + + if (pIoc1) { + pci_free_consistent(ioc->pcidev, iocpage1sz, pIoc1, ioc1_dma); + pIoc1 = NULL; + } + + return; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * SendEventNotification - Send EventNotification (on or off) request + * to MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * @EvSwitch: Event switch flags + */ +static int +SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch) +{ + EventNotification_t *evnp; + + evnp = (EventNotification_t *) mpt_get_msg_frame(mpt_base_index, ioc->id); + if (evnp == NULL) { + dprintk((MYIOC_s_WARN_FMT "Unable to allocate event request frame!\n", + ioc->name)); + return 0; + } + memset(evnp, 0, sizeof(*evnp)); + + dprintk((MYIOC_s_INFO_FMT "Sending EventNotification(%d)\n", ioc->name, EvSwitch)); + + evnp->Function = MPI_FUNCTION_EVENT_NOTIFICATION; + evnp->ChainOffset = 0; + evnp->MsgFlags = 0; + evnp->Switch = EvSwitch; + + mpt_put_msg_frame(mpt_base_index, ioc->id, (MPT_FRAME_HDR *)evnp); + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * SendEventAck - Send EventAck request to MPT adapter. + * @ioc: Pointer to MPT_ADAPTER structure + * @evnp: Pointer to original EventNotification request + */ +static int +SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp) +{ + EventAck_t *pAck; + + if ((pAck = (EventAck_t *) mpt_get_msg_frame(mpt_base_index, ioc->id)) == NULL) { + printk(MYIOC_s_WARN_FMT "Unable to allocate event ACK request frame!\n", + ioc->name); + return -1; + } + memset(pAck, 0, sizeof(*pAck)); + + dprintk((MYIOC_s_INFO_FMT "Sending EventAck\n", ioc->name)); + + pAck->Function = MPI_FUNCTION_EVENT_ACK; + pAck->ChainOffset = 0; + pAck->MsgFlags = 0; + pAck->Event = evnp->Event; + pAck->EventContext = evnp->EventContext; + + mpt_put_msg_frame(mpt_base_index, ioc->id, (MPT_FRAME_HDR *)pAck); + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_config - Generic function to issue config message + * @ioc - Pointer to an adapter structure + * @cfg - Pointer to a configuration structure. Struct contains + * action, page address, direction, physical address + * and pointer to a configuration page header + * Page header is updated. + * + * Returns 0 for success + * -EPERM if not allowed due to ISR context + * -EAGAIN if no msg frames currently available + * -EFAULT for non-successful reply or no reply (timeout) + */ +int +mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg) +{ + Config_t *pReq; + MPT_FRAME_HDR *mf; + unsigned long flags; + int ii, rc; + int flagsLength; + int in_isr; + + /* (Bugzilla:fibrebugs, #513) + * Bug fix (part 1)! 20010905 -sralston + * Prevent calling wait_event() (below), if caller happens + * to be in ISR context, because that is fatal! + */ + in_isr = in_interrupt(); + if (in_isr) { + dcprintk((MYIOC_s_WARN_FMT "Config request not allowed in ISR context!\n", + ioc->name)); + return -EPERM; + } + + /* Get and Populate a free Frame + */ + if ((mf = mpt_get_msg_frame(mpt_base_index, ioc->id)) == NULL) { + dcprintk((MYIOC_s_WARN_FMT "mpt_config: no msg frames!\n", + ioc->name)); + return -EAGAIN; + } + pReq = (Config_t *)mf; + pReq->Action = pCfg->action; + pReq->Reserved = 0; + pReq->ChainOffset = 0; + pReq->Function = MPI_FUNCTION_CONFIG; + pReq->Reserved1[0] = 0; + pReq->Reserved1[1] = 0; + pReq->Reserved1[2] = 0; + pReq->MsgFlags = 0; + for (ii=0; ii < 8; ii++) + pReq->Reserved2[ii] = 0; + + pReq->Header.PageVersion = pCfg->hdr->PageVersion; + pReq->Header.PageLength = pCfg->hdr->PageLength; + pReq->Header.PageNumber = pCfg->hdr->PageNumber; + pReq->Header.PageType = (pCfg->hdr->PageType & MPI_CONFIG_PAGETYPE_MASK); + pReq->PageAddress = cpu_to_le32(pCfg->pageAddr); + + /* Add a SGE to the config request. + */ + if (pCfg->dir) + flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE; + else + flagsLength = MPT_SGE_FLAGS_SSIMPLE_READ; + + flagsLength |= pCfg->hdr->PageLength * 4; + + mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, pCfg->physAddr); + + dcprintk((MYIOC_s_INFO_FMT "Sending Config request type %d, page %d and action %d\n", + ioc->name, pReq->Header.PageType, pReq->Header.PageNumber, pReq->Action)); + + /* Append pCfg pointer to end of mf + */ + *((void **) (((u8 *) mf) + (ioc->req_sz - sizeof(void *)))) = (void *) pCfg; + + /* Initalize the timer + */ + init_timer(&pCfg->timer); + pCfg->timer.data = (unsigned long) ioc; + pCfg->timer.function = mpt_timer_expired; + pCfg->wait_done = 0; + + /* Set the timer; ensure 10 second minimum */ + if (pCfg->timeout < 10) + pCfg->timer.expires = jiffies + HZ*10; + else + pCfg->timer.expires = jiffies + HZ*pCfg->timeout; + + /* Add to end of Q, set timer and then issue this command */ + spin_lock_irqsave(&ioc->FreeQlock, flags); + Q_ADD_TAIL(&ioc->configQ.head, &pCfg->linkage, Q_ITEM); + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + + add_timer(&pCfg->timer); + + mpt_put_msg_frame(mpt_base_index, ioc->id, mf); +/* SAE: No wait queues */ +#ifdef XENO_KILLED + wait_event(mpt_waitq, pCfg->wait_done); +#else + /* SAE: We'll just wait a bit */ + while(!pCfg->wait_done) { + mdelay(10); + } +#endif + + /* mf has been freed - do not access */ + + rc = pCfg->status; + + return rc; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_timer_expired - Call back for timer process. + * Used only internal config functionality. + * @data: Pointer to MPT_SCSI_HOST recast as an unsigned long + */ +static void +mpt_timer_expired(unsigned long data) +{ + MPT_ADAPTER *ioc = (MPT_ADAPTER *) data; + + dcprintk((MYIOC_s_WARN_FMT "mpt_timer_expired! \n", ioc->name)); + + /* Perform a FW reload */ + if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0) + printk(MYIOC_s_WARN_FMT "Firmware Reload FAILED!\n", ioc->name); + + /* No more processing. + * Hard reset clean-up will wake up + * process and free all resources. + */ + dcprintk((MYIOC_s_WARN_FMT "mpt_timer_expired complete!\n", ioc->name)); + + return; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_ioc_reset - Base cleanup for hard reset + * @ioc: Pointer to the adapter structure + * @reset_phase: Indicates pre- or post-reset functionality + * + * Remark: Free's resources with internally generated commands. + */ +static int +mpt_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) +{ + CONFIGPARMS *pCfg; + unsigned long flags; + + dprintk((KERN_WARNING MYNAM + ": IOC %s_reset routed to MPT base driver!\n", + reset_phase==MPT_IOC_SETUP_RESET ? "setup" : ( + reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"))); + + if (reset_phase == MPT_IOC_SETUP_RESET) { + ; + } else if (reset_phase == MPT_IOC_PRE_RESET) { + /* If the internal config Q is not empty - + * delete timer. MF resources will be freed when + * the FIFO's are primed. + */ + spin_lock_irqsave(&ioc->FreeQlock, flags); + if (! Q_IS_EMPTY(&ioc->configQ)){ + pCfg = (CONFIGPARMS *)ioc->configQ.head; + do { + del_timer(&pCfg->timer); + pCfg = (CONFIGPARMS *) (pCfg->linkage.forw); + } while (pCfg != (CONFIGPARMS *)&ioc->configQ); + } + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + + } else { + CONFIGPARMS *pNext; + + /* Search the configQ for internal commands. + * Flush the Q, and wake up all suspended threads. + */ + spin_lock_irqsave(&ioc->FreeQlock, flags); + if (! Q_IS_EMPTY(&ioc->configQ)){ + pCfg = (CONFIGPARMS *)ioc->configQ.head; + do { + pNext = (CONFIGPARMS *) pCfg->linkage.forw; + + Q_DEL_ITEM(&pCfg->linkage); + + pCfg->status = MPT_CONFIG_ERROR; + pCfg->wait_done = 1; +#ifdef XENO_KILLED + wake_up(&mpt_waitq); +#else + mdelay(250); +#endif + + pCfg = pNext; + } while (pCfg != (CONFIGPARMS *)&ioc->configQ); + } + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + } + + return 1; /* currently means nothing really */ +} + + +#ifdef CONFIG_PROC_FS /* { */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * procfs (%MPT_PROCFS_MPTBASEDIR/...) support stuff... + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * procmpt_create - Create %MPT_PROCFS_MPTBASEDIR entries. + * + * Returns 0 for success, non-zero for failure. + */ +static int +procmpt_create(void) +{ + MPT_ADAPTER *ioc; + struct proc_dir_entry *ent; + int ii; + + /* + * BEWARE: If/when MPT_PROCFS_MPTBASEDIR changes from "mpt" + * (single level) to multi level (e.g. "driver/message/fusion") + * something here needs to change. -sralston + */ + mpt_proc_root_dir = proc_mkdir(MPT_PROCFS_MPTBASEDIR, NULL); + if (mpt_proc_root_dir == NULL) + return -ENOTDIR; + + for (ii=0; ii < MPT_PROC_ENTRIES; ii++) { + ent = create_proc_entry(mpt_proc_list[ii].name, + S_IFREG|S_IRUGO, mpt_proc_root_dir); + if (!ent) { + printk(KERN_WARNING MYNAM + ": WARNING - Could not create /proc/mpt/%s entry\n", + mpt_proc_list[ii].name); + continue; + } + ent->read_proc = mpt_proc_list[ii].f; + ent->data = NULL; + } + + ioc = mpt_adapter_find_first(); + while (ioc != NULL) { + struct proc_dir_entry *dent; + /* + * Create "/proc/mpt/iocN" subdirectory entry for each MPT adapter. + */ + if ((dent = proc_mkdir(ioc->name, mpt_proc_root_dir)) != NULL) { + /* + * And populate it with mpt_ioc_proc_list[] entries. + */ + for (ii=0; ii < MPT_IOC_PROC_ENTRIES; ii++) { + ent = create_proc_entry(mpt_ioc_proc_list[ii].name, + S_IFREG|S_IRUGO, dent); + if (!ent) { + printk(KERN_WARNING MYNAM + ": WARNING - Could not create /proc/mpt/%s/%s entry!\n", + ioc->name, + mpt_ioc_proc_list[ii].name); + continue; + } + ent->read_proc = mpt_ioc_proc_list[ii].f; + ent->data = ioc; + } + } else { + printk(MYIOC_s_WARN_FMT "Could not create /proc/mpt/%s subdir entry!\n", + ioc->name, mpt_ioc_proc_list[ii].name); + } + ioc = mpt_adapter_find_next(ioc); + } + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * procmpt_destroy - Tear down %MPT_PROCFS_MPTBASEDIR entries. + * + * Returns 0 for success, non-zero for failure. + */ +static int +procmpt_destroy(void) +{ + MPT_ADAPTER *ioc; + int ii; + + if (!mpt_proc_root_dir) + return 0; + + /* + * BEWARE: If/when MPT_PROCFS_MPTBASEDIR changes from "mpt" + * (single level) to multi level (e.g. "driver/message/fusion") + * something here needs to change. -sralston + */ + + ioc = mpt_adapter_find_first(); + while (ioc != NULL) { + char pname[32]; + int namelen; + + namelen = sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s", ioc->name); + + /* + * Tear down each "/proc/mpt/iocN" subdirectory. + */ + for (ii=0; ii < MPT_IOC_PROC_ENTRIES; ii++) { + (void) sprintf(pname+namelen, "/%s", mpt_ioc_proc_list[ii].name); + remove_proc_entry(pname, NULL); + } + + remove_proc_entry(ioc->name, mpt_proc_root_dir); + + ioc = mpt_adapter_find_next(ioc); + } + + for (ii=0; ii < MPT_PROC_ENTRIES; ii++) + remove_proc_entry(mpt_proc_list[ii].name, mpt_proc_root_dir); + + if (atomic_read((atomic_t *)&mpt_proc_root_dir->count) == 0) { + remove_proc_entry(MPT_PROCFS_MPTBASEDIR, NULL); + mpt_proc_root_dir = NULL; + return 0; + } + + return -1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * procmpt_summary_read - Handle read request from /proc/mpt/summary + * or from /proc/mpt/iocN/summary. + * @buf: Pointer to area to write information + * @start: Pointer to start pointer + * @offset: Offset to start writing + * @request: + * @eof: Pointer to EOF integer + * @data: Pointer + * + * Returns number of characters written to process performing the read. + */ +static int +procmpt_summary_read(char *buf, char **start, off_t offset, int request, int *eof, void *data) +{ + MPT_ADAPTER *ioc; + char *out = buf; + int len; + + if (data == NULL) + ioc = mpt_adapter_find_first(); + else + ioc = data; + + while (ioc) { + int more = 0; + + mpt_print_ioc_summary(ioc, out, &more, 0, 1); + + out += more; + if ((out-buf) >= request) { + break; + } + + if (data == NULL) + ioc = mpt_adapter_find_next(ioc); + else + ioc = NULL; /* force exit for iocN */ + } + len = out - buf; + + MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * procmpt_version_read - Handle read request from /proc/mpt/version. + * @buf: Pointer to area to write information + * @start: Pointer to start pointer + * @offset: Offset to start writing + * @request: + * @eof: Pointer to EOF integer + * @data: Pointer + * + * Returns number of characters written to process performing the read. + */ +static int +procmpt_version_read(char *buf, char **start, off_t offset, int request, int *eof, void *data) +{ + int ii; + int scsi, lan, ctl, targ, dmp; + char *drvname; + int len; + + len = sprintf(buf, "%s-%s\n", "mptlinux", MPT_LINUX_VERSION_COMMON); + len += sprintf(buf+len, " Fusion MPT base driver\n"); + + scsi = lan = ctl = targ = dmp = 0; + for (ii=MPT_MAX_PROTOCOL_DRIVERS-1; ii; ii--) { + drvname = NULL; + if (MptCallbacks[ii]) { + switch (MptDriverClass[ii]) { + case MPTSCSIH_DRIVER: + if (!scsi++) drvname = "SCSI host"; + break; + case MPTLAN_DRIVER: + if (!lan++) drvname = "LAN"; + break; + case MPTSTM_DRIVER: + if (!targ++) drvname = "SCSI target"; + break; + case MPTCTL_DRIVER: + if (!ctl++) drvname = "ioctl"; + break; + case MPTDMP_DRIVER: + if (!dmp++) drvname = "DMP"; + break; + } + + if (drvname) + len += sprintf(buf+len, " Fusion MPT %s driver\n", drvname); + /* + * Handle isense special case, because it + * doesn't do a formal mpt_register call. + */ + if (isense_idx == ii) + len += sprintf(buf+len, " Fusion MPT isense driver\n"); + } + } + + MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * procmpt_iocinfo_read - Handle read request from /proc/mpt/iocN/info. + * @buf: Pointer to area to write information + * @start: Pointer to start pointer + * @offset: Offset to start writing + * @request: + * @eof: Pointer to EOF integer + * @data: Pointer + * + * Returns number of characters written to process performing the read. + */ +static int +procmpt_iocinfo_read(char *buf, char **start, off_t offset, int request, int *eof, void *data) +{ + MPT_ADAPTER *ioc = data; + int len; + char expVer[32]; + int sz; + int p; + + mpt_get_fw_exp_ver(expVer, ioc); + + len = sprintf(buf, "%s:", ioc->name); + if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) + len += sprintf(buf+len, " (f/w download boot flag set)"); +// if (ioc->facts.IOCExceptions & MPI_IOCFACTS_EXCEPT_CONFIG_CHECKSUM_FAIL) +// len += sprintf(buf+len, " CONFIG_CHECKSUM_FAIL!"); + + len += sprintf(buf+len, "\n ProductID = 0x%04x (%s)\n", + ioc->facts.ProductID, + ioc->prod_name); + len += sprintf(buf+len, " FWVersion = 0x%08x%s", ioc->facts.FWVersion.Word, expVer); + if (ioc->facts.FWImageSize) + len += sprintf(buf+len, " (fw_size=%d)", ioc->facts.FWImageSize); + len += sprintf(buf+len, "\n MsgVersion = 0x%04x\n", ioc->facts.MsgVersion); + len += sprintf(buf+len, " FirstWhoInit = 0x%02x\n", ioc->FirstWhoInit); + len += sprintf(buf+len, " EventState = 0x%02x\n", ioc->facts.EventState); + + len += sprintf(buf+len, " CurrentHostMfaHighAddr = 0x%08x\n", + ioc->facts.CurrentHostMfaHighAddr); + len += sprintf(buf+len, " CurrentSenseBufferHighAddr = 0x%08x\n", + ioc->facts.CurrentSenseBufferHighAddr); + + len += sprintf(buf+len, " MaxChainDepth = 0x%02x frames\n", ioc->facts.MaxChainDepth); + len += sprintf(buf+len, " MinBlockSize = 0x%02x bytes\n", 4*ioc->facts.BlockSize); + + len += sprintf(buf+len, " RequestFrames @ 0x%p (Dma @ 0x%p)\n", + (void *)ioc->req_alloc, (void *)(ulong)ioc->req_alloc_dma); + /* + * Rounding UP to nearest 4-kB boundary here... + */ + sz = (ioc->req_sz * ioc->req_depth) + 128; + sz = ((sz + 0x1000UL - 1UL) / 0x1000) * 0x1000; + len += sprintf(buf+len, " {CurReqSz=%d} x {CurReqDepth=%d} = %d bytes ^= 0x%x\n", + ioc->req_sz, ioc->req_depth, ioc->req_sz*ioc->req_depth, sz); + len += sprintf(buf+len, " {MaxReqSz=%d} {MaxReqDepth=%d}\n", + 4*ioc->facts.RequestFrameSize, + ioc->facts.GlobalCredits); + + len += sprintf(buf+len, " ReplyFrames @ 0x%p (Dma @ 0x%p)\n", + (void *)ioc->reply_alloc, (void *)(ulong)ioc->reply_alloc_dma); + sz = (ioc->reply_sz * ioc->reply_depth) + 128; + len += sprintf(buf+len, " {CurRepSz=%d} x {CurRepDepth=%d} = %d bytes ^= 0x%x\n", + ioc->reply_sz, ioc->reply_depth, ioc->reply_sz*ioc->reply_depth, sz); + len += sprintf(buf+len, " {MaxRepSz=%d} {MaxRepDepth=%d}\n", + ioc->facts.CurReplyFrameSize, + ioc->facts.ReplyQueueDepth); + + len += sprintf(buf+len, " MaxDevices = %d\n", + (ioc->facts.MaxDevices==0) ? 255 : ioc->facts.MaxDevices); + len += sprintf(buf+len, " MaxBuses = %d\n", ioc->facts.MaxBuses); + + /* per-port info */ + for (p=0; p < ioc->facts.NumberOfPorts; p++) { + len += sprintf(buf+len, " PortNumber = %d (of %d)\n", + p+1, + ioc->facts.NumberOfPorts); + if ((int)ioc->chip_type <= (int)FC929) { + if (ioc->pfacts[p].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN) { + u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow; + len += sprintf(buf+len, " LanAddr = %02X:%02X:%02X:%02X:%02X:%02X\n", + a[5], a[4], a[3], a[2], a[1], a[0]); + } + len += sprintf(buf+len, " WWN = %08X%08X:%08X%08X\n", + ioc->fc_port_page0[p].WWNN.High, + ioc->fc_port_page0[p].WWNN.Low, + ioc->fc_port_page0[p].WWPN.High, + ioc->fc_port_page0[p].WWPN.Low); + } + } + + MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len); +} + +#endif /* CONFIG_PROC_FS } */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static void +mpt_get_fw_exp_ver(char *buf, MPT_ADAPTER *ioc) +{ + buf[0] ='\0'; + if ((ioc->facts.FWVersion.Word >> 24) == 0x0E) { + sprintf(buf, " (Exp %02d%02d)", + (ioc->facts.FWVersion.Word >> 16) & 0x00FF, /* Month */ + (ioc->facts.FWVersion.Word >> 8) & 0x1F); /* Day */ + + /* insider hack! */ + if ((ioc->facts.FWVersion.Word >> 8) & 0x80) + strcat(buf, " [MDBG]"); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_print_ioc_summary - Write ASCII summary of IOC to a buffer. + * @ioc: Pointer to MPT_ADAPTER structure + * @buffer: Pointer to buffer where IOC summary info should be written + * @size: Pointer to number of bytes we wrote (set by this routine) + * @len: Offset at which to start writing in buffer + * @showlan: Display LAN stuff? + * + * This routine writes (english readable) ASCII text, which represents + * a summary of IOC information, to a buffer. + */ +void +mpt_print_ioc_summary(MPT_ADAPTER *ioc, char *buffer, int *size, int len, int showlan) +{ + char expVer[32]; + int y; + + mpt_get_fw_exp_ver(expVer, ioc); + + /* + * Shorter summary of attached ioc's... + */ + y = sprintf(buffer+len, "%s: %s, %s%08xh%s, Ports=%d, MaxQ=%d", + ioc->name, + ioc->prod_name, + MPT_FW_REV_MAGIC_ID_STRING, /* "FwRev=" or somesuch */ + ioc->facts.FWVersion.Word, + expVer, + ioc->facts.NumberOfPorts, + ioc->req_depth); + + if (showlan && (ioc->pfacts[0].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_LAN)) { + u8 *a = (u8*)&ioc->lan_cnfg_page1.HardwareAddressLow; + y += sprintf(buffer+len+y, ", LanAddr=%02X:%02X:%02X:%02X:%02X:%02X", + a[5], a[4], a[3], a[2], a[1], a[0]); + } + +#ifndef __sparc__ + y += sprintf(buffer+len+y, ", IRQ=%d", ioc->pci_irq); +#else + y += sprintf(buffer+len+y, ", IRQ=%s", __irq_itoa(ioc->pci_irq)); +#endif + + if (!ioc->active) + y += sprintf(buffer+len+y, " (disabled)"); + + y += sprintf(buffer+len+y, "\n"); + + *size = y; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Reset Handling + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_HardResetHandler - Generic reset handler, issue SCSI Task + * Management call based on input arg values. If TaskMgmt fails, + * return associated SCSI request. + * @ioc: Pointer to MPT_ADAPTER structure + * @sleepFlag: Indicates if sleep or schedule must be called. + * + * Remark: _HardResetHandler can be invoked from an interrupt thread (timer) + * or a non-interrupt thread. In the former, must not call schedule(). + * + * Remark: A return of -1 is a FATAL error case, as it means a + * FW reload/initialization failed. + * + * Returns 0 for SUCCESS or -1 if FAILED. + */ +int +mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag) +{ + int rc; + unsigned long flags; + + dprintk((MYIOC_s_INFO_FMT "HardResetHandler Entered!\n", ioc->name)); +#ifdef MFCNT + printk(MYIOC_s_INFO_FMT "HardResetHandler Entered!\n", ioc->name); + printk("MF count 0x%x !\n", ioc->mfcnt); +#endif + + /* Reset the adapter. Prevent more than 1 call to + * mpt_do_ioc_recovery at any instant in time. + */ + spin_lock_irqsave(&ioc->diagLock, flags); + if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)){ + spin_unlock_irqrestore(&ioc->diagLock, flags); + return 0; + } else { + ioc->diagPending = 1; + } + spin_unlock_irqrestore(&ioc->diagLock, flags); + + /* FIXME: If do_ioc_recovery fails, repeat.... + */ + + /* The SCSI driver needs to adjust timeouts on all current + * commands prior to the diagnostic reset being issued. + * Prevents timeouts occuring during a diagnostic reset...very bad. + * For all other protocol drivers, this is a no-op. + */ + { + int ii; + int r = 0; + + for (ii=MPT_MAX_PROTOCOL_DRIVERS-1; ii; ii--) { + if (MptResetHandlers[ii]) { + dprintk((MYIOC_s_INFO_FMT "Calling IOC reset_setup handler #%d\n", + ioc->name, ii)); + r += (*(MptResetHandlers[ii]))(ioc, MPT_IOC_SETUP_RESET); + if (ioc->alt_ioc) { + dprintk((MYIOC_s_INFO_FMT "Calling alt-%s setup reset handler #%d\n", + ioc->name, ioc->alt_ioc->name, ii)); + r += (*(MptResetHandlers[ii]))(ioc->alt_ioc, MPT_IOC_SETUP_RESET); + } + } + } + } + + if ((rc = mpt_do_ioc_recovery(ioc, MPT_HOSTEVENT_IOC_RECOVER, sleepFlag)) != 0) { + printk(KERN_WARNING MYNAM ": WARNING - (%d) Cannot recover %s\n", + rc, ioc->name); + } + ioc->reload_fw = 0; + if (ioc->alt_ioc) + ioc->alt_ioc->reload_fw = 0; + + spin_lock_irqsave(&ioc->diagLock, flags); + ioc->diagPending = 0; + if (ioc->alt_ioc) + ioc->alt_ioc->diagPending = 0; + spin_unlock_irqrestore(&ioc->diagLock, flags); + + dprintk((MYIOC_s_INFO_FMT "HardResetHandler rc = %d!\n", ioc->name, rc)); + + return rc; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static char * +EventDescriptionStr(u8 event, u32 evData0) +{ + char *ds = NULL; + + switch(event) { + case MPI_EVENT_NONE: + ds = "None"; + break; + case MPI_EVENT_LOG_DATA: + ds = "Log Data"; + break; + case MPI_EVENT_STATE_CHANGE: + ds = "State Change"; + break; + case MPI_EVENT_UNIT_ATTENTION: + ds = "Unit Attention"; + break; + case MPI_EVENT_IOC_BUS_RESET: + ds = "IOC Bus Reset"; + break; + case MPI_EVENT_EXT_BUS_RESET: + ds = "External Bus Reset"; + break; + case MPI_EVENT_RESCAN: + ds = "Bus Rescan Event"; + /* Ok, do we need to do anything here? As far as + I can tell, this is when a new device gets added + to the loop. */ + break; + case MPI_EVENT_LINK_STATUS_CHANGE: + if (evData0 == MPI_EVENT_LINK_STATUS_FAILURE) + ds = "Link Status(FAILURE) Change"; + else + ds = "Link Status(ACTIVE) Change"; + break; + case MPI_EVENT_LOOP_STATE_CHANGE: + if (evData0 == MPI_EVENT_LOOP_STATE_CHANGE_LIP) + ds = "Loop State(LIP) Change"; + else if (evData0 == MPI_EVENT_LOOP_STATE_CHANGE_LPE) + ds = "Loop State(LPE) Change"; /* ??? */ + else + ds = "Loop State(LPB) Change"; /* ??? */ + break; + case MPI_EVENT_LOGOUT: + ds = "Logout"; + break; + case MPI_EVENT_EVENT_CHANGE: + if (evData0) + ds = "Events(ON) Change"; + else + ds = "Events(OFF) Change"; + break; + case MPI_EVENT_INTEGRATED_RAID: + ds = "Integrated Raid"; + break; + /* + * MPT base "custom" events may be added here... + */ + default: + ds = "Unknown"; + break; + } + return ds; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * ProcessEventNotification - Route a received EventNotificationReply to + * all currently regeistered event handlers. + * @ioc: Pointer to MPT_ADAPTER structure + * @pEventReply: Pointer to EventNotification reply frame + * @evHandlers: Pointer to integer, number of event handlers + * + * Returns sum of event handlers return values. + */ +static int +ProcessEventNotification(MPT_ADAPTER *ioc, EventNotificationReply_t *pEventReply, int *evHandlers) +{ + u16 evDataLen; + u32 evData0 = 0; +// u32 evCtx; + int ii; + int r = 0; + int handlers = 0; + char *evStr; + u8 event; + + /* + * Do platform normalization of values + */ + event = le32_to_cpu(pEventReply->Event) & 0xFF; +// evCtx = le32_to_cpu(pEventReply->EventContext); + evDataLen = le16_to_cpu(pEventReply->EventDataLength); + if (evDataLen) { + evData0 = le32_to_cpu(pEventReply->Data[0]); + } + + evStr = EventDescriptionStr(event, evData0); + dprintk((MYIOC_s_INFO_FMT "MPT event (%s=%02Xh) detected!\n", + ioc->name, + evStr, + event)); + +#if defined(MPT_DEBUG) || defined(MPT_DEBUG_EVENTS) + printk(KERN_INFO MYNAM ": Event data:\n" KERN_INFO); + for (ii = 0; ii < evDataLen; ii++) + printk(" %08x", le32_to_cpu(pEventReply->Data[ii])); + printk("\n"); +#endif + + /* + * Do general / base driver event processing + */ + switch(event) { + case MPI_EVENT_NONE: /* 00 */ + case MPI_EVENT_LOG_DATA: /* 01 */ + case MPI_EVENT_STATE_CHANGE: /* 02 */ + case MPI_EVENT_UNIT_ATTENTION: /* 03 */ + case MPI_EVENT_IOC_BUS_RESET: /* 04 */ + case MPI_EVENT_EXT_BUS_RESET: /* 05 */ + case MPI_EVENT_RESCAN: /* 06 */ + case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */ + case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */ + case MPI_EVENT_LOGOUT: /* 09 */ + case MPI_EVENT_INTEGRATED_RAID: /* 0B */ + case MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE: /* 0C */ + default: + break; + case MPI_EVENT_EVENT_CHANGE: /* 0A */ + if (evDataLen) { + u8 evState = evData0 & 0xFF; + + /* CHECKME! What if evState unexpectedly says OFF (0)? */ + + /* Update EventState field in cached IocFacts */ + if (ioc->facts.Function) { + ioc->facts.EventState = evState; + } + } + break; + } + + /* + * Should this event be logged? Events are written sequentially. + * When buffer is full, start again at the top. + */ + if (ioc->events && (ioc->eventTypes & ( 1 << event))) { + int idx; + + idx = ioc->eventContext % ioc->eventLogSize; + + ioc->events[idx].event = event; + ioc->events[idx].eventContext = ioc->eventContext; + + for (ii = 0; ii < 2; ii++) { + if (ii < evDataLen) + ioc->events[idx].data[ii] = le32_to_cpu(pEventReply->Data[ii]); + else + ioc->events[idx].data[ii] = 0; + } + + ioc->eventContext++; + } + + + /* + * Call each currently registered protocol event handler. + */ + for (ii=MPT_MAX_PROTOCOL_DRIVERS-1; ii; ii--) { + if (MptEvHandlers[ii]) { + dprintk((MYIOC_s_INFO_FMT "Routing Event to event handler #%d\n", + ioc->name, ii)); + r += (*(MptEvHandlers[ii]))(ioc, pEventReply); + handlers++; + } + } + /* FIXME? Examine results here? */ + + /* + * If needed, send (a single) EventAck. + */ + if (pEventReply->AckRequired == MPI_EVENT_NOTIFICATION_ACK_REQUIRED) { + if ((ii = SendEventAck(ioc, pEventReply)) != 0) { + printk(MYIOC_s_WARN_FMT "SendEventAck returned %d\n", + ioc->name, ii); + } + } + + *evHandlers = handlers; + return r; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_fc_log_info - Log information returned from Fibre Channel IOC. + * @ioc: Pointer to MPT_ADAPTER structure + * @log_info: U32 LogInfo reply word from the IOC + * + * Refer to lsi/fc_log.h. + */ +static void +mpt_fc_log_info(MPT_ADAPTER *ioc, u32 log_info) +{ + static char *subcl_str[8] = { + "FCP Initiator", "FCP Target", "LAN", "MPI Message Layer", + "FC Link", "Context Manager", "Invalid Field Offset", "State Change Info" + }; + char *desc = "unknown"; + u8 subcl = (log_info >> 24) & 0x7; + u32 SubCl = log_info & 0x27000000; + + switch(log_info) { +/* FCP Initiator */ + case MPI_IOCLOGINFO_FC_INIT_ERROR_OUT_OF_ORDER_FRAME: + desc = "Received an out of order frame - unsupported"; + break; + case MPI_IOCLOGINFO_FC_INIT_ERROR_BAD_START_OF_FRAME: + desc = "Bad start of frame primative"; + break; + case MPI_IOCLOGINFO_FC_INIT_ERROR_BAD_END_OF_FRAME: + desc = "Bad end of frame primative"; + break; + case MPI_IOCLOGINFO_FC_INIT_ERROR_OVER_RUN: + desc = "Receiver hardware detected overrun"; + break; + case MPI_IOCLOGINFO_FC_INIT_ERROR_RX_OTHER: + desc = "Other errors caught by IOC which require retries"; + break; + case MPI_IOCLOGINFO_FC_INIT_ERROR_SUBPROC_DEAD: + desc = "Main processor could not initialize sub-processor"; + break; +/* FC Target */ + case MPI_IOCLOGINFO_FC_TARGET_NO_PDISC: + desc = "Not sent because we are waiting for a PDISC from the initiator"; + break; + case MPI_IOCLOGINFO_FC_TARGET_NO_LOGIN: + desc = "Not sent because we are not logged in to the remote node"; + break; + case MPI_IOCLOGINFO_FC_TARGET_DOAR_KILLED_BY_LIP: + desc = "Data Out, Auto Response, not sent due to a LIP"; + break; + case MPI_IOCLOGINFO_FC_TARGET_DIAR_KILLED_BY_LIP: + desc = "Data In, Auto Response, not sent due to a LIP"; + break; + case MPI_IOCLOGINFO_FC_TARGET_DIAR_MISSING_DATA: + desc = "Data In, Auto Response, missing data frames"; + break; + case MPI_IOCLOGINFO_FC_TARGET_DONR_KILLED_BY_LIP: + desc = "Data Out, No Response, not sent due to a LIP"; + break; + case MPI_IOCLOGINFO_FC_TARGET_WRSP_KILLED_BY_LIP: + desc = "Auto-response after a write not sent due to a LIP"; + break; + case MPI_IOCLOGINFO_FC_TARGET_DINR_KILLED_BY_LIP: + desc = "Data In, No Response, not completed due to a LIP"; + break; + case MPI_IOCLOGINFO_FC_TARGET_DINR_MISSING_DATA: + desc = "Data In, No Response, missing data frames"; + break; + case MPI_IOCLOGINFO_FC_TARGET_MRSP_KILLED_BY_LIP: + desc = "Manual Response not sent due to a LIP"; + break; + case MPI_IOCLOGINFO_FC_TARGET_NO_CLASS_3: + desc = "Not sent because remote node does not support Class 3"; + break; + case MPI_IOCLOGINFO_FC_TARGET_LOGIN_NOT_VALID: + desc = "Not sent because login to remote node not validated"; + break; + case MPI_IOCLOGINFO_FC_TARGET_FROM_OUTBOUND: + desc = "Cleared from the outbound queue after a logout"; + break; + case MPI_IOCLOGINFO_FC_TARGET_WAITING_FOR_DATA_IN: + desc = "Cleared waiting for data after a logout"; + break; +/* LAN */ + case MPI_IOCLOGINFO_FC_LAN_TRANS_SGL_MISSING: + desc = "Transaction Context Sgl Missing"; + break; + case MPI_IOCLOGINFO_FC_LAN_TRANS_WRONG_PLACE: + desc = "Transaction Context found before an EOB"; + break; + case MPI_IOCLOGINFO_FC_LAN_TRANS_RES_BITS_SET: + desc = "Transaction Context value has reserved bits set"; + break; + case MPI_IOCLOGINFO_FC_LAN_WRONG_SGL_FLAG: + desc = "Invalid SGL Flags"; + break; +/* FC Link */ + case MPI_IOCLOGINFO_FC_LINK_LOOP_INIT_TIMEOUT: + desc = "Loop initialization timed out"; + break; + case MPI_IOCLOGINFO_FC_LINK_ALREADY_INITIALIZED: + desc = "Another system controller already initialized the loop"; + break; + case MPI_IOCLOGINFO_FC_LINK_LINK_NOT_ESTABLISHED: + desc = "Not synchronized to signal or still negotiating (possible cable problem)"; + break; + case MPI_IOCLOGINFO_FC_LINK_CRC_ERROR: + desc = "CRC check detected error on received frame"; + break; + } + + printk(MYIOC_s_INFO_FMT "LogInfo(0x%08x): SubCl={%s}", + ioc->name, log_info, subcl_str[subcl]); + if (SubCl == MPI_IOCLOGINFO_FC_INVALID_FIELD_BYTE_OFFSET) + printk(", byte_offset=%d\n", log_info & MPI_IOCLOGINFO_FC_INVALID_FIELD_MAX_OFFSET); + else if (SubCl == MPI_IOCLOGINFO_FC_STATE_CHANGE) + printk("\n"); /* StateChg in LogInfo & 0x00FFFFFF, above */ + else + printk("\n" KERN_INFO " %s\n", desc); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mpt_sp_log_info - Log information returned from SCSI Parallel IOC. + * @ioc: Pointer to MPT_ADAPTER structure + * @mr: Pointer to MPT reply frame + * @log_info: U32 LogInfo word from the IOC + * + * Refer to lsi/sp_log.h. + */ +static void +mpt_sp_log_info(MPT_ADAPTER *ioc, u32 log_info) +{ + u32 info = log_info & 0x00FF0000; + char *desc = "unknown"; + + switch (info) { + case 0x00010000: + desc = "bug! MID not found"; + if (ioc->reload_fw == 0) + ioc->reload_fw++; + break; + + case 0x00020000: + desc = "Parity Error"; + break; + + case 0x00030000: + desc = "ASYNC Outbound Overrun"; + break; + + case 0x00040000: + desc = "SYNC Offset Error"; + break; + + case 0x00050000: + desc = "BM Change"; + break; + + case 0x00060000: + desc = "Msg In Overflow"; + break; + + case 0x00070000: + desc = "DMA Error"; + break; + + case 0x00080000: + desc = "Outbound DMA Overrun"; + break; + } + + printk(MYIOC_s_INFO_FMT "LogInfo(0x%08x): F/W: %s\n", ioc->name, log_info, desc); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_register_ascqops_strings - Register SCSI ASC/ASCQ and SCSI + * OpCode strings from the (optional) isense module. + * @ascqTable: Pointer to ASCQ_Table_t structure + * @ascqtbl_sz: Number of entries in ASCQ_Table + * @opsTable: Pointer to array of SCSI OpCode strings (char pointers) + * + * Specialized driver registration routine for the isense driver. + */ +int +mpt_register_ascqops_strings(void *ascqTable, int ascqtbl_sz, const char **opsTable) +{ + int r = 0; + + if (ascqTable && ascqtbl_sz && opsTable) { + mpt_v_ASCQ_TablePtr = ascqTable; + mpt_ASCQ_TableSz = ascqtbl_sz; + mpt_ScsiOpcodesPtr = opsTable; + printk(KERN_INFO MYNAM ": English readable SCSI-3 strings enabled:-)\n"); + isense_idx = last_drv_idx; + r = 1; + } + mpt_inc_use_count(); + return r; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mpt_deregister_ascqops_strings - Deregister SCSI ASC/ASCQ and SCSI + * OpCode strings from the isense driver. + * + * Specialized driver deregistration routine for the isense driver. + */ +void +mpt_deregister_ascqops_strings(void) +{ + mpt_v_ASCQ_TablePtr = NULL; + mpt_ASCQ_TableSz = 0; + mpt_ScsiOpcodesPtr = NULL; + printk(KERN_INFO MYNAM ": English readable SCSI-3 strings disabled)-:\n"); + isense_idx = -1; + mpt_dec_use_count(); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +EXPORT_SYMBOL(mpt_adapters); +EXPORT_SYMBOL(mpt_proc_root_dir); +EXPORT_SYMBOL(DmpService); +EXPORT_SYMBOL(mpt_register); +EXPORT_SYMBOL(mpt_deregister); +EXPORT_SYMBOL(mpt_event_register); +EXPORT_SYMBOL(mpt_event_deregister); +EXPORT_SYMBOL(mpt_reset_register); +EXPORT_SYMBOL(mpt_reset_deregister); +EXPORT_SYMBOL(mpt_get_msg_frame); +EXPORT_SYMBOL(mpt_put_msg_frame); +EXPORT_SYMBOL(mpt_free_msg_frame); +EXPORT_SYMBOL(mpt_add_sge); +EXPORT_SYMBOL(mpt_add_chain); +EXPORT_SYMBOL(mpt_send_handshake_request); +EXPORT_SYMBOL(mpt_handshake_req_reply_wait); +EXPORT_SYMBOL(mpt_adapter_find_first); +EXPORT_SYMBOL(mpt_adapter_find_next); +EXPORT_SYMBOL(mpt_verify_adapter); +EXPORT_SYMBOL(mpt_GetIocState); +EXPORT_SYMBOL(mpt_print_ioc_summary); +EXPORT_SYMBOL(mpt_lan_index); +EXPORT_SYMBOL(mpt_stm_index); +EXPORT_SYMBOL(mpt_HardResetHandler); +EXPORT_SYMBOL(mpt_config); +EXPORT_SYMBOL(mpt_read_ioc_pg_3); +EXPORT_SYMBOL(mpt_alloc_fw_memory); +EXPORT_SYMBOL(mpt_free_fw_memory); + +EXPORT_SYMBOL(mpt_register_ascqops_strings); +EXPORT_SYMBOL(mpt_deregister_ascqops_strings); +EXPORT_SYMBOL(mpt_v_ASCQ_TablePtr); +EXPORT_SYMBOL(mpt_ASCQ_TableSz); +EXPORT_SYMBOL(mpt_ScsiOpcodesPtr); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * fusion_init - Fusion MPT base driver initialization routine. + * + * Returns 0 for success, non-zero for failure. + */ +int __init +fusion_init(void) +{ + int i; + + if (FusionInitCalled++) { + dprintk((KERN_INFO MYNAM ": INFO - Driver late-init entry point called\n")); + return 0; + } + + Q_INIT(&MptAdapters, MPT_ADAPTER); /* set to empty */ + for (i = 0; i < MPT_MAX_PROTOCOL_DRIVERS; i++) { + MptCallbacks[i] = NULL; + MptDriverClass[i] = MPTUNKNOWN_DRIVER; + MptEvHandlers[i] = NULL; + MptResetHandlers[i] = NULL; + } + + DmpService = NULL; + + /* NEW! 20010120 -sralston + * Register ourselves (mptbase) in order to facilitate + * EventNotification handling. + */ + mpt_base_index = mpt_register(mpt_base_reply, MPTBASE_DRIVER); + + /* Register for hard reset handling callbacks. + */ + if (mpt_reset_register(mpt_base_index, mpt_ioc_reset) == 0) { + dprintk((KERN_INFO MYNAM ": Register for IOC reset notification\n")); + } else { + /* FIXME! */ + } + + if ((i = mpt_pci_scan()) < 0) + return i; + + show_mptmod_ver(my_NAME, my_VERSION); + printk(KERN_INFO COPYRIGHT "\n"); + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * fusion_exit - Perform driver unload cleanup. + * + * This routine frees all resources associated with each MPT adapter + * and removes all %MPT_PROCFS_MPTBASEDIR entries. + */ +static void +fusion_exit(void) +{ + MPT_ADAPTER *this; + struct pci_dev *pdev = NULL; + + dprintk((KERN_INFO MYNAM ": fusion_exit() called!\n")); + + /* Whups? 20010120 -sralston + * Moved this *above* removal of all MptAdapters! + */ +#ifdef CONFIG_PROC_FS + (void) procmpt_destroy(); +#endif + + while (! Q_IS_EMPTY(&MptAdapters)) { + this = MptAdapters.head; + + /* Disable interrupts! */ + CHIPREG_WRITE32(&this->chip->IntMask, 0xFFFFFFFF); + + this->active = 0; + + pdev = (struct pci_dev *)this->pcidev; + mptscsih_sync_irq(pdev->irq); + + /* Clear any lingering interrupt */ + CHIPREG_WRITE32(&this->chip->IntStatus, 0); + + CHIPREG_READ32(&this->chip->IntStatus); + + Q_DEL_ITEM(this); + mpt_adapter_dispose(this); + } + + mpt_reset_deregister(mpt_base_index); +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +module_init(fusion_init); +module_exit(fusion_exit); diff --git a/xen/drivers/message/fusion/mptbase.h b/xen/drivers/message/fusion/mptbase.h new file mode 100644 index 0000000000..02e6e4d731 --- /dev/null +++ b/xen/drivers/message/fusion/mptbase.h @@ -0,0 +1,1123 @@ +/* + * linux/drivers/message/fusion/mptbase.h + * High performance SCSI + LAN / Fibre Channel device drivers. + * For use with PCI chip/adapter(s): + * LSIFC9xx/LSI409xx Fibre Channel + * running LSI Logic Fusion MPT (Message Passing Technology) firmware. + * + * Credits: + * (see mptbase.c) + * + * Copyright (c) 1999-2002 LSI Logic Corporation + * Originally By: Steven J. Ralston + * (mailto:sjralston1@netscape.net) + * (mailto:lstephens@lsil.com) + * + * $Id: mptbase.h,v 1.149 2003/05/07 14:08:31 pdelaney Exp $ + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MPTBASE_H_INCLUDED +#define MPTBASE_H_INCLUDED +/*{-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include + +#include "linux_compat.h" /* linux-2.2.x (vs. -2.4.x) tweaks */ +#include "scsi3.h" /* SCSI defines */ + +#include "lsi/mpi_type.h" +#include "lsi/mpi.h" /* Fusion MPI(nterface) basic defs */ +#include "lsi/mpi_ioc.h" /* Fusion MPT IOC(ontroller) defs */ +#include "lsi/mpi_cnfg.h" /* IOC configuration support */ +#include "lsi/mpi_init.h" /* SCSI Host (initiator) protocol support */ +#include "lsi/mpi_lan.h" /* LAN over FC protocol support */ +#include "lsi/mpi_raid.h" /* Integrated Mirroring support */ + +#include "lsi/mpi_fc.h" /* Fibre Channel (lowlevel) support */ +#include "lsi/mpi_targ.h" /* SCSI/FCP Target protcol support */ +#include "lsi/fc_log.h" + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#ifndef MODULEAUTHOR +#define MODULEAUTHOR "LSI Logic Corporation" +#endif + +#ifndef COPYRIGHT +#define COPYRIGHT "Copyright (c) 1999-2002 " MODULEAUTHOR +#endif + +#define MPT_LINUX_VERSION_COMMON "2.05.05+" +#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-2.05.05+" +#define WHAT_MAGIC_STRING "@" "(" "#" ")" + +#define show_mptmod_ver(s,ver) \ + printk(KERN_INFO "%s %s\n", s, ver); + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Fusion MPT(linux) driver configurable stuff... + */ +#define MPT_MAX_ADAPTERS 18 +#define MPT_MAX_PROTOCOL_DRIVERS 16 +#define MPT_MAX_BUS 1 /* Do not change */ +#define MPT_MAX_FC_DEVICES 255 +#define MPT_MAX_SCSI_DEVICES 16 +#define MPT_LAST_LUN 31 +#define MPT_SENSE_BUFFER_ALLOC 64 + /* allow for 256 max sense alloc, but only 255 max request */ +#if MPT_SENSE_BUFFER_ALLOC >= 256 +# undef MPT_SENSE_BUFFER_ALLOC +# define MPT_SENSE_BUFFER_ALLOC 256 +# define MPT_SENSE_BUFFER_SIZE 255 +#else +# define MPT_SENSE_BUFFER_SIZE MPT_SENSE_BUFFER_ALLOC +#endif + +#define MPT_NAME_LENGTH 32 + +#define MPT_PROCFS_MPTBASEDIR "mpt" + /* chg it to "driver/fusion" ? */ +#define MPT_PROCFS_SUMMARY_ALL_NODE MPT_PROCFS_MPTBASEDIR "/summary" +#define MPT_PROCFS_SUMMARY_ALL_PATHNAME "/proc/" MPT_PROCFS_SUMMARY_ALL_NODE +#define MPT_FW_REV_MAGIC_ID_STRING "FwRev=" + +#define MPT_MAX_REQ_DEPTH 1023 +#define MPT_DEFAULT_REQ_DEPTH 256 +#define MPT_MIN_REQ_DEPTH 128 + +#define MPT_MAX_REPLY_DEPTH MPT_MAX_REQ_DEPTH +#define MPT_DEFAULT_REPLY_DEPTH 128 +#define MPT_MIN_REPLY_DEPTH 8 +#define MPT_MAX_REPLIES_PER_ISR 32 + +#define MPT_MAX_FRAME_SIZE 128 +#define MPT_DEFAULT_FRAME_SIZE 128 + +#define MPT_SG_REQ_128_SCALE 1 +#define MPT_SG_REQ_96_SCALE 2 +#define MPT_SG_REQ_64_SCALE 4 + +#define CAN_SLEEP 1 +#define NO_SLEEP 0 + +#define MPT_COALESCING_TIMEOUT 0x10 + +/* + * SCSI transfer rate defines. + */ +#define MPT_ULTRA320 0x08 +#define MPT_ULTRA160 0x09 +#define MPT_ULTRA2 0x0A +#define MPT_ULTRA 0x0C +#define MPT_FAST 0x19 +#define MPT_SCSI 0x32 +#define MPT_ASYNC 0xFF + +#define MPT_NARROW 0 +#define MPT_WIDE 1 + +#ifdef __KERNEL__ /* { */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +/* SAE: Xen doesn't have a proc filesystem */ +#if defined(CONFIG_PROC_FS) +#include +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Attempt semi-consistent error & warning msgs across + * MPT drivers. NOTE: Users of these macro defs must + * themselves define their own MYNAM. + */ +#define MYIOC_s_INFO_FMT KERN_INFO MYNAM ": %s: " +#define MYIOC_s_NOTE_FMT KERN_NOTICE MYNAM ": %s: " +#define MYIOC_s_WARN_FMT KERN_WARNING MYNAM ": %s: WARNING - " +#define MYIOC_s_ERR_FMT KERN_ERR MYNAM ": %s: ERROR - " + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * MPT protocol driver defs... + */ +typedef enum { + MPTBASE_DRIVER, /* MPT base class */ + MPTCTL_DRIVER, /* MPT ioctl class */ + MPTSCSIH_DRIVER, /* MPT SCSI host (initiator) class */ + MPTLAN_DRIVER, /* MPT LAN class */ + MPTSTM_DRIVER, /* MPT SCSI target mode class */ + MPTDMP_DRIVER, /* MPT Dynamic Multi-pathing class */ + MPTUNKNOWN_DRIVER +} MPT_DRIVER_CLASS; + +/* + * MPT adapter / port / bus / device info structures... + */ + +typedef union _MPT_FRAME_TRACKER { + struct { + struct _MPT_FRAME_HDR *forw; + struct _MPT_FRAME_HDR *back; + u32 arg1; + u32 pad; + void *argp1; +#ifndef MPT_SCSI_USE_NEW_EH + void *argp2; +#endif + } linkage; + /* + * NOTE: When request frames are free, on the linkage structure + * contets are valid. All other values are invalid. + * In particular, do NOT reply on offset [2] + * (in words) being the * message context. + * The message context must be reset (computed via base address + * + an offset) prior to issuing any command. + * + * NOTE2: On non-32-bit systems, where pointers are LARGE, + * using the linkage pointers destroys our sacred MsgContext + * field contents. But we don't care anymore because these + * are now reset in mpt_put_msg_frame() just prior to sending + * a request off to the IOC. + */ + struct { + u32 __hdr[2]; + /* + * The following _MUST_ match the location of the + * MsgContext field in the MPT message headers. + */ + union { + u32 MsgContext; + struct { + u16 req_idx; /* Request index */ + u8 cb_idx; /* callback function index */ + u8 rsvd; + } fld; + } msgctxu; + } hwhdr; + /* + * Remark: 32 bit identifier: + * 31-24: reserved + * 23-16: call back index + * 15-0 : request index + */ +} MPT_FRAME_TRACKER; + +/* + * We might want to view/access a frame as: + * 1) generic request header + * 2) SCSIIORequest + * 3) SCSIIOReply + * 4) MPIDefaultReply + * 5) frame tracker + */ +typedef struct _MPT_FRAME_HDR { + union { + MPIHeader_t hdr; + SCSIIORequest_t scsireq; + SCSIIOReply_t sreply; + MPIDefaultReply_t reply; + MPT_FRAME_TRACKER frame; + } u; +} MPT_FRAME_HDR; + +#define MPT_REQ_MSGFLAGS_DROPME 0x80 + +/* Used for tracking the free request frames + * and free reply frames. + */ +typedef struct _MPT_Q_TRACKER { + MPT_FRAME_HDR *head; + MPT_FRAME_HDR *tail; +} MPT_Q_TRACKER; + + +typedef struct _MPT_SGL_HDR { + SGESimple32_t sge[1]; +} MPT_SGL_HDR; + +typedef struct _MPT_SGL64_HDR { + SGESimple64_t sge[1]; +} MPT_SGL64_HDR; + + +typedef struct _Q_ITEM { + struct _Q_ITEM *forw; + struct _Q_ITEM *back; +} Q_ITEM; + +typedef struct _Q_TRACKER { + struct _Q_ITEM *head; + struct _Q_ITEM *tail; +} Q_TRACKER; + +typedef struct _MPT_DONE_Q { + struct _MPT_DONE_Q *forw; + struct _MPT_DONE_Q *back; + void *argp; +} MPT_DONE_Q; + +typedef struct _DONE_Q_TRACKER { + MPT_DONE_Q *head; + MPT_DONE_Q *tail; +} DONE_Q_TRACKER; + +/* + * Chip-specific stuff... FC929 delineates break between + * FC and Parallel SCSI parts. Do NOT re-order. + */ + +typedef enum { + FC919X = 0x0819, + FC929X = 0x0829, + FC909 = 0x0909, + FC919 = 0x0919, + FC929 = 0x0929, + C1030 = 0x1030, + C1035 = 0x1035, + FCUNK = 0xFBAD +} CHIP_TYPE; + +/* + * System interface register set + */ + +typedef struct _SYSIF_REGS +{ + u32 Doorbell; /* 00 System<->IOC Doorbell reg */ + u32 WriteSequence; /* 04 Write Sequence register */ + u32 Diagnostic; /* 08 Diagnostic register */ + u32 TestBase; /* 0C Test Base Address */ + u32 DiagRwData; /* 10 Read Write Data (fw download) */ + u32 DiagRwAddress; /* 14 Read Write Address (fw download)*/ + u32 Reserved1[6]; /* 18-2F reserved for future use */ + u32 IntStatus; /* 30 Interrupt Status */ + u32 IntMask; /* 34 Interrupt Mask */ + u32 Reserved2[2]; /* 38-3F reserved for future use */ + u32 RequestFifo; /* 40 Request Post/Free FIFO */ + u32 ReplyFifo; /* 44 Reply Post/Free FIFO */ + u32 Reserved3[2]; /* 48-4F reserved for future use */ + u32 HostIndex; /* 50 Host Index register */ + u32 Reserved4[15]; /* 54-8F */ + u32 Fubar; /* 90 For Fubar usage */ + u32 Reserved5[27]; /* 94-FF */ +} SYSIF_REGS; + +/* + * NOTE: Use MPI_{DOORBELL,WRITESEQ,DIAG}_xxx defs in lsi/mpi.h + * in conjunction with SYSIF_REGS accesses! + */ + + +/* + * Dynamic Multi-Pathing specific stuff... + */ +#define DMP_MAX_PATHS 8 + +typedef struct _PathInfo { + u8 ioc; + u8 target; + u8 pad; + u8 pflags; +} PathInfo; + +#define PATHINFO_FLAGS_OWNED 0x01 +#define PATHINFO_FLAGS_EXISTS 0x02 +#define PATHINFO_FLAGS_AVAILABLE 0x04 +#define PATHINFO_FLAGS_SECONDARY 0x08 + +#define PFLAGS_EXISTS_AND_AVAIL (PATHINFO_FLAGS_EXISTS|PATHINFO_FLAGS_AVAILABLE) +#define PFLAGS_AVAIL_AND_OWNED (PATHINFO_FLAGS_AVAILABLE|PATHINFO_FLAGS_OWNED) + +typedef struct _ScsiCmndTracker { + void *head; + void *tail; +} ScsiCmndTracker; + + +/* + * VirtDevice - FC LUN device or SCSI target device + * (used to be FCSCSI_TARGET) + */ +typedef struct _VirtDevice { + struct _VirtDevice *forw; + struct _VirtDevice *back; + struct scsi_device *device; + rwlock_t VdevLock; + int ref_cnt; + u8 tflags; + u8 ioc_id; + u8 target_id; + u8 bus_id; + u8 minSyncFactor; /* 0xFF is async */ + u8 maxOffset; /* 0 if async */ + u8 maxWidth; /* 0 if narrow, 1 if wide*/ + u8 negoFlags; /* bit field, 0 if WDTR/SDTR/QAS allowed */ + u8 raidVolume; /* set, if RAID Volume */ + u8 type; /* byte 0 of Inquiry data */ + u8 cflags; /* controller flags */ + u8 rsvd1raid; + int npaths; + u16 fc_phys_lun; + u16 fc_xlat_lun; + int stall_detected; + PathInfo path[DMP_MAX_PATHS]; + struct timer_list stall_timer; + struct timer_list retry_timer; + struct timer_list gone_timer; + ScsiCmndTracker WaitQ; + ScsiCmndTracker SentQ; + ScsiCmndTracker DoneQ; + u32 num_luns; +//--- LUN split here? + u32 luns; /* Max LUNs is 32 */ + u8 inq_data[SCSI_STD_INQUIRY_BYTES]; /* 36 */ + u8 pad0[4]; + u8 inq00_data[20]; + u8 pad1[4]; + /* IEEE Registered Extended Identifier + obtained via INQUIRY VPD page 0x83 */ + /* NOTE: Do not separate uniq_prepad and uniq_data + as they are treateed as a single entity in the code */ + u8 uniq_prepad[8]; + u8 uniq_data[20]; + u8 pad2[4]; + u8 inqC3_data[12]; + u8 pad3[4]; + u8 inqC9_data[12]; + u8 pad4[4]; + u8 dev_vol_name[64]; +} VirtDevice; + +/* + * Fibre Channel (SCSI) target device and associated defines... + */ +#define MPT_TARGET_DEFAULT_DV_STATUS 0 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,55) +#define MPT_TARGET_FLAGS_CONFIGURED 0x02 +#define MPT_TARGET_FLAGS_Q_YES 0x08 +#else +#define MPT_TARGET_FLAGS_VALID_NEGO 0x01 +#define MPT_TARGET_FLAGS_VALID_INQUIRY 0x02 +#define MPT_TARGET_FLAGS_Q_YES 0x08 +#define MPT_TARGET_FLAGS_VALID_56 0x10 +#endif + +#define MPT_TARGET_NO_NEGO_WIDE 0x01 +#define MPT_TARGET_NO_NEGO_SYNC 0x02 +#define MPT_TARGET_NO_NEGO_QAS 0x04 + +typedef struct _VirtDevTracker { + struct _VirtDevice *head; + struct _VirtDevice *tail; + rwlock_t VlistLock; + int pad; +} VirtDevTracker; + +/* SAE: Xen doesn't have proc */ +#if defined(CONFIG_PROC_FS) + +/* + * /proc/mpt interface + */ +typedef struct { + const char *name; + mode_t mode; + int pad; + read_proc_t *read_proc; + write_proc_t *write_proc; +} mpt_proc_entry_t; + +#define MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len) \ +do { \ + len -= offset; \ + if (len < request) { \ + *eof = 1; \ + if (len <= 0) \ + return 0; \ + } else \ + len = request; \ + *start = buf + offset; \ + return len; \ +} while (0) + +#endif + +/* + * IOCTL structure and associated defines + */ + +#define MPT_IOCTL_STATUS_DID_IOCRESET 0x01 /* IOC Reset occurred on the current*/ +#define MPT_IOCTL_STATUS_RF_VALID 0x02 /* The Reply Frame is VALID */ +#define MPT_IOCTL_STATUS_TIMER_ACTIVE 0x04 /* The timer is running */ +#define MPT_IOCTL_STATUS_SENSE_VALID 0x08 /* Sense data is valid */ +#define MPT_IOCTL_STATUS_COMMAND_GOOD 0x10 /* Command Status GOOD */ +#define MPT_IOCTL_STATUS_TMTIMER_ACTIVE 0x20 /* The TM timer is running */ +#define MPT_IOCTL_STATUS_TM_FAILED 0x40 /* User TM request failed */ + +#define MPTCTL_RESET_OK 0x01 /* Issue Bus Reset */ + +typedef struct _MPT_IOCTL { + struct _MPT_ADAPTER *ioc; + struct timer_list timer; /* timer function for this adapter */ + u8 ReplyFrame[MPT_DEFAULT_FRAME_SIZE]; /* reply frame data */ + u8 sense[MPT_SENSE_BUFFER_ALLOC]; + int wait_done; /* wake-up value for this ioc */ + u8 rsvd; + u8 status; /* current command status */ + u8 reset; /* 1 if bus reset allowed */ + u8 target; /* target for reset */ + void *tmPtr; + struct timer_list TMtimer; /* timer function for this adapter */ +} MPT_IOCTL; + +/* + * Event Structure and define + */ +#define MPTCTL_EVENT_LOG_SIZE (0x0000000A) +typedef struct _mpt_ioctl_events { + u32 event; /* Specified by define above */ + u32 eventContext; /* Index or counter */ + int data[2]; /* First 8 bytes of Event Data */ +} MPT_IOCTL_EVENTS; + +/* + * CONFIGPARM status defines + */ +#define MPT_CONFIG_GOOD MPI_IOCSTATUS_SUCCESS +#define MPT_CONFIG_ERROR 0x002F + +/* + * Substructure to store SCSI specific configuration page data + */ + /* dvStatus defines: */ +#define MPT_SCSICFG_NEGOTIATE 0x01 /* Negotiate on next IO */ +#define MPT_SCSICFG_NEED_DV 0x02 /* Schedule DV */ +#define MPT_SCSICFG_DV_PENDING 0x04 /* DV on this physical id pending */ +#define MPT_SCSICFG_DV_NOT_DONE 0x08 /* DV has not been performed */ +#define MPT_SCSICFG_BLK_NEGO 0x10 /* WriteSDP1 with WDTR and SDTR disabled */ +#define MPT_SCSICFG_RELOAD_IOC_PG3 0x20 /* IOC Pg 3 data is obsolete */ + /* Args passed to writeSDP1: */ +#define MPT_SCSICFG_USE_NVRAM 0x01 /* WriteSDP1 using NVRAM */ +#define MPT_SCSICFG_ALL_IDS 0x02 /* WriteSDP1 to all IDS */ +/* #define MPT_SCSICFG_BLK_NEGO 0x10 WriteSDP1 with WDTR and SDTR disabled */ + +typedef struct _ScsiCfgData { + int *nvram; /* table of device NVRAM values */ + IOCPage3_t *pIocPg3; /* table of physical disks */ + u8 dvStatus[MPT_MAX_SCSI_DEVICES]; + int isRaid; /* bit field, 1 if RAID */ + u8 minSyncFactor; /* 0xFF if async */ + u8 maxSyncOffset; /* 0 if async */ + u8 maxBusWidth; /* 0 if narrow, 1 if wide */ + u8 busType; /* SE, LVD, HD */ + u8 sdp1version; /* SDP1 version */ + u8 sdp1length; /* SDP1 length */ + u8 sdp0version; /* SDP0 version */ + u8 sdp0length; /* SDP0 length */ + u8 dvScheduled; /* 1 if scheduled */ + u8 forceDv; /* 1 to force DV scheduling */ + u8 noQas; /* Disable QAS for this adapter */ + u8 rsvd[2]; +} ScsiCfgData; + +typedef struct _fw_image { + char *fw; + dma_addr_t fw_dma; + u32 size; + u32 rsvd; +} fw_image_t; + +/* + * Adapter Structure - pci_dev specific. Maximum: MPT_MAX_ADAPTERS + */ +typedef struct _MPT_ADAPTER +{ + struct _MPT_ADAPTER *forw; + struct _MPT_ADAPTER *back; + int id; /* Unique adapter id N {0,1,2,...} */ + int pci_irq; /* This irq */ + char name[MPT_NAME_LENGTH]; /* "iocN" */ + char *prod_name; /* "LSIFC9x9" */ + volatile SYSIF_REGS *chip; /* == c8817000 (mmap) */ + volatile SYSIF_REGS *pio_chip; /* Programmed IO (downloadboot) */ + u32 mem_phys; /* == f4020000 (mmap) */ + u32 pio_mem_phys; /* Programmed IO (downloadboot) */ + int mem_size; /* mmap memory size */ + int alloc_total; + u32 last_state; + int active; + u8 *reply_alloc; /* Reply frames alloc ptr */ + dma_addr_t reply_alloc_dma; + MPT_FRAME_HDR *reply_frames; /* Reply msg frames - rounded up! */ + dma_addr_t reply_frames_dma; + u32 reply_frames_low_dma; + int reply_depth; /* Num Allocated reply frames */ + int reply_sz; /* Reply frame size */ + CHIP_TYPE chip_type; + /* We (host driver) get to manage our own RequestQueue! */ + u8 *req_alloc; /* Request frames alloc ptr */ + dma_addr_t req_alloc_dma; + MPT_FRAME_HDR *req_frames; /* Request msg frames - rounded up! */ + dma_addr_t req_frames_dma; + u32 req_frames_low_dma; + int req_depth; /* Number of request frames */ + int req_sz; /* Request frame size (bytes) */ + spinlock_t FreeQlock; + MPT_Q_TRACKER FreeQ; + /* Pool of SCSI sense buffers for commands coming from + * the SCSI mid-layer. We have one 256 byte sense buffer + * for each REQ entry. + */ + u8 *sense_buf_pool; + dma_addr_t sense_buf_pool_dma; + u32 sense_buf_low_dma; + int mtrr_reg; + struct pci_dev *pcidev; /* struct pci_dev pointer */ + u8 *memmap; /* mmap address */ + struct Scsi_Host *sh; /* Scsi Host pointer */ + ScsiCfgData spi_data; /* Scsi config. data */ + MPT_IOCTL *ioctl; /* ioctl data pointer */ + struct proc_dir_entry *ioc_dentry; + struct _MPT_ADAPTER *alt_ioc; /* ptr to 929 bound adapter port */ + spinlock_t diagLock; /* diagnostic reset lock */ + int diagPending; + u32 biosVersion; /* BIOS version from IO Unit Page 2 */ + int eventTypes; /* Event logging parameters */ + int eventContext; /* Next event context */ + int eventLogSize; /* Max number of cached events */ +#ifdef MPTSCSIH_DBG_TIMEOUT + int timeout_hard; + int timeout_delta; + int timeout_cnt; + int timeout_maxcnt; +#endif + struct _mpt_ioctl_events *events; /* pointer to event log */ + fw_image_t **cached_fw; /* Pointer to FW SG List */ + Q_TRACKER configQ; /* linked list of config. requests */ + int num_fw_frags; /* Number of SGE in FW SG List */ + int hs_reply_idx; +#ifndef MFCNT + u32 pad0; +#else + u32 mfcnt; +#endif + u32 hs_req[MPT_MAX_FRAME_SIZE/sizeof(u32)]; + u16 hs_reply[MPT_MAX_FRAME_SIZE/sizeof(u16)]; + IOCFactsReply_t facts; + PortFactsReply_t pfacts[2]; + FCPortPage0_t fc_port_page0[2]; + LANPage0_t lan_cnfg_page0; + LANPage1_t lan_cnfg_page1; + u8 FirstWhoInit; + u8 upload_fw; /* If set, do a fw upload */ + u8 reload_fw; /* Force a FW Reload on next reset */ + u8 pad1[5]; +} MPT_ADAPTER; + + +typedef struct _MPT_ADAPTER_TRACKER { + MPT_ADAPTER *head; + MPT_ADAPTER *tail; +} MPT_ADAPTER_TRACKER; + +/* + * New return value convention: + * 1 = Ok to free associated request frame + * 0 = not Ok ... + */ +typedef int (*MPT_CALLBACK)(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply); +typedef int (*MPT_EVHANDLER)(MPT_ADAPTER *ioc, EventNotificationReply_t *evReply); +typedef int (*MPT_RESETHANDLER)(MPT_ADAPTER *ioc, int reset_phase); +/* reset_phase defs */ +#define MPT_IOC_PRE_RESET 0 +#define MPT_IOC_POST_RESET 1 +#define MPT_IOC_SETUP_RESET 2 + +/* + * Invent MPT host event (super-set of MPI Events) + * Fitted to 1030's 64-byte [max] request frame size + */ +typedef struct _MPT_HOST_EVENT { + EventNotificationReply_t MpiEvent; /* 8 32-bit words! */ + u32 pad[6]; + void *next; +} MPT_HOST_EVENT; + +#define MPT_HOSTEVENT_IOC_BRINGUP 0x91 +#define MPT_HOSTEVENT_IOC_RECOVER 0x92 + +/* Define the generic types based on the size + * of the dma_addr_t type. + */ +typedef struct _mpt_sge { + u32 FlagsLength; + dma_addr_t Address; +} MptSge_t; + +#define mpt_addr_size() \ + ((sizeof(dma_addr_t) == sizeof(u64)) ? MPI_SGE_FLAGS_64_BIT_ADDRESSING : \ + MPI_SGE_FLAGS_32_BIT_ADDRESSING) + +#define mpt_msg_flags() \ + ((sizeof(dma_addr_t) == sizeof(u64)) ? MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_64 : \ + MPI_SCSIIO_MSGFLGS_SENSE_WIDTH_32) + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Funky (private) macros... + */ +#ifdef MPT_DEBUG +#define dprintk(x) printk x +#else +#define dprintk(x) +#endif + +#ifdef MPT_DEBUG_HANDSHAKE +#define dhsprintk(x) printk x +#else +#define dhsprintk(x) +#endif + +//#if defined(MPT_DEBUG) || defined(MPT_DEBUG_MSG_FRAME) +#ifdef MPT_DEBUG_MSG_FRAME +#define dmfprintk(x) printk x +#else +#define dmfprintk(x) +#endif + +#ifdef MPT_DEBUG_IRQ +#define dirqprintk(x) printk x +#else +#define dirqprintk(x) +#endif + +#ifdef MPT_DEBUG_SG +#define dsgprintk(x) printk x +#else +#define dsgprintk(x) +#endif + +#if defined(MPT_DEBUG_DL) +//#if defined(MPT_DEBUG_DL) || defined(MPT_DEBUG) +#define ddlprintk(x) printk x +#else +#define ddlprintk(x) +#endif + + +#ifdef MPT_DEBUG_DV +#define ddvprintk(x) printk x +#else +#define ddvprintk(x) +#endif + +#if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY) +#define ddvtprintk(x) printk x +#else +#define ddvtprintk(x) +#endif + +#ifdef MPT_DEBUG_IOCTL +#define dctlprintk(x) printk x +#else +#define dctlprintk(x) +#endif + +#ifdef MPT_DEBUG_RESET +#define dtmprintk(x) printk x +#else +#define dtmprintk(x) +#endif + +#ifdef MPT_DEBUG_NEH +#define nehprintk(x) printk x +#else +#define nehprintk(x) +#endif + +#if defined(MPT_DEBUG_CONFIG) +//#if defined(MPT_DEBUG_CONFIG) || defined(MPT_DEBUG) +#define dcprintk(x) printk x +#else +#define dcprintk(x) +#endif + +#if defined(MPT_DEBUG_SCSI) || defined(MPT_DEBUG) || defined(MPT_DEBUG_MSG_FRAME) +#define dsprintk(x) printk x +#else +#define dsprintk(x) +#endif + + +#define MPT_INDEX_2_MFPTR(ioc,idx) \ + (MPT_FRAME_HDR*)( (u8*)(ioc)->req_frames + (ioc)->req_sz * (idx) ) + +#define MFPTR_2_MPT_INDEX(ioc,mf) \ + (int)( ((u8*)mf - (u8*)(ioc)->req_frames) / (ioc)->req_sz ) + +#define MPT_INDEX_2_RFPTR(ioc,idx) \ + (MPT_FRAME_HDR*)( (u8*)(ioc)->reply_frames + (ioc)->req_sz * (idx) ) + +#define Q_INIT(q,type) (q)->head = (q)->tail = (type*)(q) +#define Q_IS_EMPTY(q) ((Q_ITEM*)(q)->head == (Q_ITEM*)(q)) + +#define Q_ADD_TAIL(qt,i,type) { \ + Q_TRACKER *_qt = (Q_TRACKER*)(qt); \ + Q_ITEM *oldTail = _qt->tail; \ + (i)->forw = (type*)_qt; \ + (i)->back = (type*)oldTail; \ + oldTail->forw = (Q_ITEM*)(i); \ + _qt->tail = (Q_ITEM*)(i); \ +} + +#define Q_ADD_HEAD(qt,i,type) { \ + Q_TRACKER *_qt = (Q_TRACKER*)(qt); \ + Q_ITEM *oldHead = _qt->head; \ + (i)->forw = (type*)oldHead; \ + (i)->back = (type*)_qt; \ + oldHead->back = (Q_ITEM*)(i); \ + _qt->head = (Q_ITEM*)(i); \ +} + +#define Q_DEL_ITEM(i) { \ + Q_ITEM *_forw = (Q_ITEM*)(i)->forw; \ + Q_ITEM *_back = (Q_ITEM*)(i)->back; \ + _back->forw = _forw; \ + _forw->back = _back; \ +} + +#define SWAB4(value) \ + (u32)( (((value) & 0x000000ff) << 24) \ + | (((value) & 0x0000ff00) << 8) \ + | (((value) & 0x00ff0000) >> 8) \ + | (((value) & 0xff000000) >> 24) ) + + +#if defined(MPT_DEBUG) || defined(MPT_DEBUG_MSG_FRAME) +#define DBG_DUMP_REPLY_FRAME(mfp) \ + { u32 *m = (u32 *)(mfp); \ + int i, n = (le32_to_cpu(m[0]) & 0x00FF0000) >> 16; \ + printk(KERN_INFO " "); \ + for (i=0; iSCp area + * NOTE: SCp area is 36 bytes min, 44 bytes max? + */ +typedef struct _scPrivate { + struct scsi_cmnd *forw; + struct scsi_cmnd *back; + void *p1; + void *p2; + u8 io_path_id; /* DMP */ + u8 pad[7]; +} scPrivate; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * More Dynamic Multi-Pathing stuff... + */ + +/* Forward decl, a strange C thing, to prevent gcc compiler warnings */ +struct scsi_cmnd; + +/* + * DMP service layer structure / API interface + */ +typedef struct _DmpServices { + VirtDevTracker VdevList; + struct semaphore *Daemon; + int (*ScsiPathSelect) + (struct scsi_cmnd *, MPT_SCSI_HOST **hd, int *target, int *lun); + int (*DmpIoDoneChk) + (MPT_SCSI_HOST *, struct scsi_cmnd *, + SCSIIORequest_t *, + SCSIIOReply_t *); + void (*mptscsih_scanVlist) + (MPT_SCSI_HOST *, int portnum); + int (*ScsiAbort) + (struct scsi_cmnd *); + int (*ScsiBusReset) + (struct scsi_cmnd *); +} DmpServices_t; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Generic structure passed to the base mpt_config function. + */ +typedef struct _x_config_parms { + Q_ITEM linkage; /* linked list */ + struct timer_list timer; /* timer function for this request */ + ConfigPageHeader_t *hdr; + dma_addr_t physAddr; + int wait_done; /* wait for this request */ + u32 pageAddr; /* properly formatted */ + u8 action; + u8 dir; + u8 timeout; /* seconds */ + u8 pad1; + u16 status; + u16 pad2; +} CONFIGPARMS; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Public entry points... + */ +extern int mpt_register(MPT_CALLBACK cbfunc, MPT_DRIVER_CLASS dclass); +extern void mpt_deregister(int cb_idx); +extern int mpt_event_register(int cb_idx, MPT_EVHANDLER ev_cbfunc); +extern void mpt_event_deregister(int cb_idx); +extern int mpt_reset_register(int cb_idx, MPT_RESETHANDLER reset_func); +extern void mpt_reset_deregister(int cb_idx); +extern int mpt_register_ascqops_strings(void *ascqTable, int ascqtbl_sz, const char **opsTable); +extern void mpt_deregister_ascqops_strings(void); +extern MPT_FRAME_HDR *mpt_get_msg_frame(int handle, int iocid); +extern void mpt_free_msg_frame(int handle, int iocid, MPT_FRAME_HDR *mf); +extern void mpt_put_msg_frame(int handle, int iocid, MPT_FRAME_HDR *mf); +extern void mpt_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr); +extern void mpt_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr); + +extern int mpt_send_handshake_request(int handle, int iocid, int reqBytes, u32 *req, int sleepFlag); +extern int mpt_handshake_req_reply_wait(MPT_ADAPTER *ioc, int reqBytes, u32 *req, int replyBytes, u16 *u16reply, int maxwait, int sleepFlag); +extern int mpt_verify_adapter(int iocid, MPT_ADAPTER **iocpp); +extern MPT_ADAPTER *mpt_adapter_find_first(void); +extern MPT_ADAPTER *mpt_adapter_find_next(MPT_ADAPTER *prev); +extern u32 mpt_GetIocState(MPT_ADAPTER *ioc, int cooked); +extern void mpt_print_ioc_summary(MPT_ADAPTER *ioc, char *buf, int *size, int len, int showlan); +extern int mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag); +extern int mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *cfg); +extern void *mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size, int *frags, int *alloc_sz); +extern void mpt_free_fw_memory(MPT_ADAPTER *ioc, fw_image_t **alt_img); +extern int mpt_read_ioc_pg_3(MPT_ADAPTER *ioc); + +/* + * Public data decl's... + */ +extern MPT_ADAPTER *mpt_adapters[MPT_MAX_ADAPTERS]; +extern struct proc_dir_entry *mpt_proc_root_dir; +extern DmpServices_t *DmpService; + +extern int mpt_lan_index; /* needed by mptlan.c */ +extern int mpt_stm_index; /* needed by mptstm.c */ + +extern void *mpt_v_ASCQ_TablePtr; +extern const char **mpt_ScsiOpcodesPtr; +extern int mpt_ASCQ_TableSz; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#endif /* } __KERNEL__ */ + +/* + * More (public) macros... + */ +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MAX +#define MAX(a, b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef offsetof +#define offsetof(t, m) ((size_t) (&((t *)0)->m)) +#endif + +#if defined(__alpha__) || defined(__sparc_v9__) || defined(__ia64__) || defined(__x86_64__) +#define CAST_U32_TO_PTR(x) ((void *)(u64)x) +#define CAST_PTR_TO_U32(x) ((u32)(u64)x) +#else +#define CAST_U32_TO_PTR(x) ((void *)x) +#define CAST_PTR_TO_U32(x) ((u32)x) +#endif + +#define MPT_PROTOCOL_FLAGS_c_c_c_c(pflags) \ + ((pflags) & MPI_PORTFACTS_PROTOCOL_INITIATOR) ? 'I' : 'i', \ + ((pflags) & MPI_PORTFACTS_PROTOCOL_TARGET) ? 'T' : 't', \ + ((pflags) & MPI_PORTFACTS_PROTOCOL_LAN) ? 'L' : 'l', \ + ((pflags) & MPI_PORTFACTS_PROTOCOL_LOGBUSADDR) ? 'B' : 'b' + +/* + * Shifted SGE Defines - Use in SGE with FlagsLength member. + * Otherwise, use MPI_xxx defines (refer to "lsi/mpi.h" header). + * Defaults: 32 bit SGE, SYSTEM_ADDRESS if direction bit is 0, read + */ +#define MPT_TRANSFER_IOC_TO_HOST (0x00000000) +#define MPT_TRANSFER_HOST_TO_IOC (0x04000000) +#define MPT_SGE_FLAGS_LAST_ELEMENT (0x80000000) +#define MPT_SGE_FLAGS_END_OF_BUFFER (0x40000000) +#define MPT_SGE_FLAGS_LOCAL_ADDRESS (0x08000000) +#define MPT_SGE_FLAGS_DIRECTION (0x04000000) +#define MPT_SGE_FLAGS_ADDRESSING (mpt_addr_size() << MPI_SGE_FLAGS_SHIFT) +#define MPT_SGE_FLAGS_END_OF_LIST (0x01000000) + +#define MPT_SGE_FLAGS_TRANSACTION_ELEMENT (0x00000000) +#define MPT_SGE_FLAGS_SIMPLE_ELEMENT (0x10000000) +#define MPT_SGE_FLAGS_CHAIN_ELEMENT (0x30000000) +#define MPT_SGE_FLAGS_ELEMENT_MASK (0x30000000) + +#define MPT_SGE_FLAGS_SSIMPLE_READ \ + (MPT_SGE_FLAGS_LAST_ELEMENT | \ + MPT_SGE_FLAGS_END_OF_BUFFER | \ + MPT_SGE_FLAGS_END_OF_LIST | \ + MPT_SGE_FLAGS_SIMPLE_ELEMENT | \ + MPT_SGE_FLAGS_ADDRESSING | \ + MPT_TRANSFER_IOC_TO_HOST) +#define MPT_SGE_FLAGS_SSIMPLE_WRITE \ + (MPT_SGE_FLAGS_LAST_ELEMENT | \ + MPT_SGE_FLAGS_END_OF_BUFFER | \ + MPT_SGE_FLAGS_END_OF_LIST | \ + MPT_SGE_FLAGS_SIMPLE_ELEMENT | \ + MPT_SGE_FLAGS_ADDRESSING | \ + MPT_TRANSFER_HOST_TO_IOC) + +/*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#endif + diff --git a/xen/drivers/message/fusion/mptscsih.c b/xen/drivers/message/fusion/mptscsih.c new file mode 100644 index 0000000000..5a8a78ad33 --- /dev/null +++ b/xen/drivers/message/fusion/mptscsih.c @@ -0,0 +1,7948 @@ +/* + * linux/drivers/message/fusion/mptscsih.c + * High performance SCSI / Fibre Channel SCSI Host device driver. + * For use with PCI chip/adapter(s): + * LSIFC9xx/LSI409xx Fibre Channel + * running LSI Logic Fusion MPT (Message Passing Technology) firmware. + * + * Credits: + * This driver would not exist if not for Alan Cox's development + * of the linux i2o driver. + * + * A special thanks to Pamela Delaney (LSI Logic) for tons of work + * and countless enhancements while adding support for the 1030 + * chip family. Pam has been instrumental in the development of + * of the 2.xx.xx series fusion drivers, and her contributions are + * far too numerous to hope to list in one place. + * + * A huge debt of gratitude is owed to David S. Miller (DaveM) + * for fixing much of the stupid and broken stuff in the early + * driver while porting to sparc64 platform. THANK YOU! + * + * (see mptbase.c) + * + * Copyright (c) 1999-2002 LSI Logic Corporation + * Original author: Steven J. Ralston + * (mailto:sjralston1@netscape.net) + * (mailto:lstephens@lsil.com) + * + * $Id: mptscsih.c,v 1.1.2.4 2003/05/07 14:08:34 pdelaney Exp $ + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include /* SAE: Necessary... */ +#include +#include +#include +#include +#include +#include +#include +#include /* for io_request_lock (spinlock) decl */ +#include /* for mdelay */ +#include /* needed for in_interrupt() proto */ +#include /* notifier code */ +#include +#include /* SAE: Necessary... */ +#include "../../scsi/scsi.h" +#include "../../scsi/hosts.h" +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45) +#include "../../scsi/sd.h" +#endif + +#include "mptbase.h" +#include "mptscsih.h" +#include "isense.h" + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#define my_NAME "Fusion MPT SCSI Host driver" +#define my_VERSION MPT_LINUX_VERSION_COMMON +#define MYNAM "mptscsih" + +MODULE_AUTHOR(MODULEAUTHOR); +MODULE_DESCRIPTION(my_NAME); +MODULE_LICENSE("GPL"); + +/* Set string for command line args from insmod */ +#ifdef MODULE +char *mptscsih = 0; +MODULE_PARM(mptscsih, "s"); +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +typedef struct _BIG_SENSE_BUF { + u8 data[MPT_SENSE_BUFFER_ALLOC]; +} BIG_SENSE_BUF; + +#define MPT_SCANDV_GOOD (0x00000000) /* must be 0 */ +#define MPT_SCANDV_DID_RESET (0x00000001) +#define MPT_SCANDV_SENSE (0x00000002) +#define MPT_SCANDV_SOME_ERROR (0x00000004) +#define MPT_SCANDV_SELECTION_TIMEOUT (0x00000008) +#define MPT_SCANDV_ISSUE_SENSE (0x00000010) + +#define MPT_SCANDV_MAX_RETRIES (10) + +#define MPT_ICFLAG_BUF_CAP 0x01 /* ReadBuffer Read Capacity format */ +#define MPT_ICFLAG_ECHO 0x02 /* ReadBuffer Echo buffer format */ +#define MPT_ICFLAG_PHYS_DISK 0x04 /* Any SCSI IO but do Phys Disk Format */ +#define MPT_ICFLAG_TAGGED_CMD 0x08 /* Do tagged IO */ +#define MPT_ICFLAG_DID_RESET 0x20 /* Bus Reset occured with this command */ +#define MPT_ICFLAG_RESERVED 0x40 /* Reserved has been issued */ + +typedef struct _internal_cmd { + char *data; /* data pointer */ + dma_addr_t data_dma; /* data dma address */ + int size; /* transfer size */ + u8 cmd; /* SCSI Op Code */ + u8 bus; /* bus number */ + u8 id; /* SCSI ID (virtual) */ + u8 lun; + u8 flags; /* Bit Field - See above */ + u8 physDiskNum; /* Phys disk number, -1 else */ + u8 rsvd2; + u8 rsvd; +} INTERNAL_CMD; + +typedef struct _negoparms { + u8 width; + u8 offset; + u8 factor; + u8 flags; +} NEGOPARMS; + +typedef struct _dv_parameters { + NEGOPARMS max; + NEGOPARMS now; + u8 cmd; + u8 id; + u16 pad1; +} DVPARAMETERS; + + +/* + * Other private/forward protos... + */ +static int mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r); +static void mptscsih_report_queue_full(Scsi_Cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq); +static int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r); + +static int mptscsih_AddSGE(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt, + SCSIIORequest_t *pReq, int req_idx); +static void mptscsih_freeChainBuffers(MPT_SCSI_HOST *hd, int req_idx); +static int mptscsih_initChainBuffers (MPT_SCSI_HOST *hd, int init); +static void copy_sense_data(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply); +#ifndef MPT_SCSI_USE_NEW_EH +static void search_taskQ_for_cmd(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd); +#else +static int mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd); +#endif +static u32 SCPNT_TO_LOOKUP_IDX(Scsi_Cmnd *sc); +static MPT_FRAME_HDR *mptscsih_search_pendingQ(MPT_SCSI_HOST *hd, int scpnt_idx); +static void post_pendingQ_commands(MPT_SCSI_HOST *hd); + +static int mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, int sleepFlag); +static int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, int sleepFlag); + +static int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset); +static int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply); + +static VirtDevice *mptscsih_initTarget(MPT_SCSI_HOST *hd, int bus_id, int target_id, u8 lun, char *data, int dlen); +void mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56); +static void mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq); +static void mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags); +static void mptscsih_no_negotiate(MPT_SCSI_HOST *hd, int target_id); +static int mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target, int flags); +static int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r); +static void mptscsih_timer_expired(unsigned long data); +static void mptscsih_taskmgmt_timeout(unsigned long data); +static int mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd); +static int mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, int portnum); + +#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION +static int mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io); +static void mptscsih_domainValidation(void *hd); +static int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id); +static void mptscsih_qas_check(MPT_SCSI_HOST *hd, int id); +static int mptscsih_doDv(MPT_SCSI_HOST *hd, int channel, int target); +static void mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage); +static void mptscsih_fillbuf(char *buffer, int size, int index, int width); +#endif +static int mptscsih_setup(char *str); +static int mptscsih_halt(struct notifier_block *nb, ulong event, void *buf); + +#if XENO_KILLED +/* + * Reboot Notification + */ +static struct notifier_block mptscsih_notifier = { + mptscsih_halt, NULL, 0 +}; +#endif + +/* + * Private data... + */ + +static int mpt_scsi_hosts = 0; +static atomic_t queue_depth; + +static int ScsiDoneCtx = -1; +static int ScsiTaskCtx = -1; +static int ScsiScanDvCtx = -1; /* Used only for bus scan and dv */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,28) +static struct proc_dir_entry proc_mpt_scsihost = +{ + .low_ino = PROC_SCSI_MPT, + .namelen = 8, + .name = "mptscsih", + .mode = S_IFDIR | S_IRUGO | S_IXUGO, + .nlink = 2, +}; +#endif + +#define SNS_LEN(scp) sizeof((scp)->sense_buffer) + +#ifndef MPT_SCSI_USE_NEW_EH +/* + * Stuff to handle single-threading SCSI TaskMgmt + * (abort/reset) requests... + */ +static spinlock_t mytaskQ_lock = SPIN_LOCK_UNLOCKED; +static int mytaskQ_bh_active = 0; +static struct mpt_work_struct mptscsih_ptaskfoo; +static atomic_t mpt_taskQdepth; +#endif + +#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION +/* + * Domain Validation task structure + */ +static spinlock_t dvtaskQ_lock = SPIN_LOCK_UNLOCKED; +static int dvtaskQ_active = 0; +static int dvtaskQ_release = 0; +static struct mpt_work_struct mptscsih_dvTask; +#endif + +/* SAE: No wait queues in Xen */ +#ifdef XENO_KILLED +/* + * Wait Queue setup + */ +static DECLARE_WAIT_QUEUE_HEAD (scandv_waitq); + +#endif + +static int scandv_wait_done = 1; + +/* Driver default setup + */ +static struct mptscsih_driver_setup + driver_setup = MPTSCSIH_DRIVER_SETUP; + +#ifdef MPTSCSIH_DBG_TIMEOUT +static Scsi_Cmnd *foo_to[8]; +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Private inline routines... + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* 19991030 -sralston + * Return absolute SCSI data direction: + * 1 = _DATA_OUT + * 0 = _DIR_NONE + * -1 = _DATA_IN + * + * Changed: 3-20-2002 pdelaney to use the default data + * direction and the defines set up in the + * 2.4 kernel series + * 1 = _DATA_OUT changed to SCSI_DATA_WRITE (1) + * 0 = _DIR_NONE changed to SCSI_DATA_NONE (3) + * -1 = _DATA_IN changed to SCSI_DATA_READ (2) + * If the direction is unknown, fall through to original code. + * + * Mid-layer bug fix(): sg interface generates the wrong data + * direction in some cases. Set the direction the hard way for + * the most common commands. + */ +static inline int +mptscsih_io_direction(Scsi_Cmnd *cmd) +{ + switch (cmd->cmnd[0]) { + case WRITE_6: + case WRITE_10: + return SCSI_DATA_WRITE; + break; + case READ_6: + case READ_10: + return SCSI_DATA_READ; + break; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) + if (cmd->sc_data_direction != SCSI_DATA_UNKNOWN) + return cmd->sc_data_direction; +#endif + switch (cmd->cmnd[0]) { + /* _DATA_OUT commands */ + case WRITE_6: case WRITE_10: case WRITE_12: + case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER: + case WRITE_VERIFY: case WRITE_VERIFY_12: + case COMPARE: case COPY: case COPY_VERIFY: + case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW: + case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12: + case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT: + case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK: + case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG: + case REASSIGN_BLOCKS: + case PERSISTENT_RESERVE_OUT: + case 0xea: + case 0xa3: + return SCSI_DATA_WRITE; + + /* No data transfer commands */ + case SEEK_6: case SEEK_10: + case RESERVE: case RELEASE: + case TEST_UNIT_READY: + case START_STOP: + case ALLOW_MEDIUM_REMOVAL: + return SCSI_DATA_NONE; + + /* Conditional data transfer commands */ + case FORMAT_UNIT: + if (cmd->cmnd[1] & 0x10) /* FmtData (data out phase)? */ + return SCSI_DATA_WRITE; + else + return SCSI_DATA_NONE; + + case VERIFY: + if (cmd->cmnd[1] & 0x02) /* VERIFY:BYTCHK (data out phase)? */ + return SCSI_DATA_WRITE; + else + return SCSI_DATA_NONE; + + case RESERVE_10: + if (cmd->cmnd[1] & 0x03) /* RESERVE:{LongID|Extent} (data out phase)? */ + return SCSI_DATA_WRITE; + else + return SCSI_DATA_NONE; + + /* Must be data _IN! */ + default: + return SCSI_DATA_READ; + } +} /* mptscsih_io_direction() */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_add_sge - Place a simple SGE at address pAddr. + * @pAddr: virtual address for SGE + * @flagslength: SGE flags and data transfer length + * @dma_addr: Physical address + * + * This routine places a MPT request frame back on the MPT adapter's + * FreeQ. + */ +static inline void +mptscsih_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr) +{ + if (sizeof(dma_addr_t) == sizeof(u64)) { + SGESimple64_t *pSge = (SGESimple64_t *) pAddr; + u32 tmp = dma_addr & 0xFFFFFFFF; + + pSge->FlagsLength = cpu_to_le32(flagslength); + pSge->Address.Low = cpu_to_le32(tmp); + tmp = (u32) ((u64)dma_addr >> 32); + pSge->Address.High = cpu_to_le32(tmp); + + } else { + SGESimple32_t *pSge = (SGESimple32_t *) pAddr; + pSge->FlagsLength = cpu_to_le32(flagslength); + pSge->Address = cpu_to_le32(dma_addr); + } +} /* mptscsih_add_sge() */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_add_chain - Place a chain SGE at address pAddr. + * @pAddr: virtual address for SGE + * @next: nextChainOffset value (u32's) + * @length: length of next SGL segment + * @dma_addr: Physical address + * + * This routine places a MPT request frame back on the MPT adapter's + * FreeQ. + */ +static inline void +mptscsih_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr) +{ + if (sizeof(dma_addr_t) == sizeof(u64)) { + SGEChain64_t *pChain = (SGEChain64_t *) pAddr; + u32 tmp = dma_addr & 0xFFFFFFFF; + + pChain->Length = cpu_to_le16(length); + pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size(); + + pChain->NextChainOffset = next; + + pChain->Address.Low = cpu_to_le32(tmp); + tmp = (u32) ((u64)dma_addr >> 32); + pChain->Address.High = cpu_to_le32(tmp); + } else { + SGEChain32_t *pChain = (SGEChain32_t *) pAddr; + pChain->Length = cpu_to_le16(length); + pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size(); + pChain->NextChainOffset = next; + pChain->Address = cpu_to_le32(dma_addr); + } +} /* mptscsih_add_chain() */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_getFreeChainBuffes - Function to get a free chain + * from the MPT_SCSI_HOST FreeChainQ. + * @hd: Pointer to the MPT_SCSI_HOST instance + * @req_idx: Index of the SCSI IO request frame. (output) + * + * return SUCCESS or FAILED + */ +static inline int +mptscsih_getFreeChainBuffer(MPT_SCSI_HOST *hd, int *retIndex) +{ + MPT_FRAME_HDR *chainBuf = NULL; + unsigned long flags; + int rc = FAILED; + int chain_idx = MPT_HOST_NO_CHAIN; + + spin_lock_irqsave(&hd->ioc->FreeQlock, flags); + if (!Q_IS_EMPTY(&hd->FreeChainQ)) { + + int offset; + + chainBuf = hd->FreeChainQ.head; + Q_DEL_ITEM(&chainBuf->u.frame.linkage); + offset = (u8 *)chainBuf - (u8 *)hd->ChainBuffer; + chain_idx = offset / hd->ioc->req_sz; + rc = SUCCESS; + } + spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); + + + *retIndex = chain_idx; + + dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer (index %d), got buf=%p\n", + hd->ioc->name, *retIndex, chainBuf)); + + return rc; +} /* mptscsih_getFreeChainBuffer() */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_AddSGE - Add a SGE (plus chain buffers) to the + * SCSIIORequest_t Message Frame. + * @hd: Pointer to MPT_SCSI_HOST structure + * @SCpnt: Pointer to Scsi_Cmnd structure + * @pReq: Pointer to SCSIIORequest_t structure + * + * Returns ... + */ +static int +mptscsih_AddSGE(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt, + SCSIIORequest_t *pReq, int req_idx) +{ + char *psge; + char *chainSge; + struct scatterlist *sg; + int frm_sz; + int sges_left, sg_done; + int chain_idx = MPT_HOST_NO_CHAIN; + int sgeOffset; + int numSgeSlots, numSgeThisFrame; + u32 sgflags, sgdir, thisxfer = 0; + int chain_dma_off = 0; + int newIndex; + int ii; + dma_addr_t v2; + + sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK; + if (sgdir == MPI_SCSIIO_CONTROL_WRITE) { + sgdir = MPT_TRANSFER_HOST_TO_IOC; + } else { + sgdir = MPT_TRANSFER_IOC_TO_HOST; + } + + psge = (char *) &pReq->SGL; + frm_sz = hd->ioc->req_sz; + + /* Map the data portion, if any. + * sges_left = 0 if no data transfer. + */ + sges_left = SCpnt->use_sg; + if (SCpnt->use_sg) { + sges_left = pci_map_sg(hd->ioc->pcidev, + (struct scatterlist *) SCpnt->request_buffer, + SCpnt->use_sg, + scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); + } else if (SCpnt->request_bufflen) { + dma_addr_t buf_dma_addr; + scPrivate *my_priv; + + buf_dma_addr = pci_map_single(hd->ioc->pcidev, + SCpnt->request_buffer, + SCpnt->request_bufflen, + scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); + + /* We hide it here for later unmap. */ + my_priv = (scPrivate *) &SCpnt->SCp; + my_priv->p1 = (void *)(ulong) buf_dma_addr; + + dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n", + hd->ioc->name, SCpnt, SCpnt->request_bufflen)); + + mptscsih_add_sge((char *) &pReq->SGL, + 0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen, + buf_dma_addr); + + return SUCCESS; + } + + /* Handle the SG case. + */ + sg = (struct scatterlist *) SCpnt->request_buffer; + sg_done = 0; + sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION); + chainSge = NULL; + + /* Prior to entering this loop - the following must be set + * current MF: sgeOffset (bytes) + * chainSge (Null if original MF is not a chain buffer) + * sg_done (num SGE done for this MF) + */ + +nextSGEset: + numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) ); + numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots; + + sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir; + + /* Get first (num - 1) SG elements + * Skip any SG entries with a length of 0 + * NOTE: at finish, sg and psge pointed to NEXT data/location positions + */ + for (ii=0; ii < (numSgeThisFrame-1); ii++) { + thisxfer = sg_dma_len(sg); + if (thisxfer == 0) { + sg ++; /* Get next SG element from the OS */ + sg_done++; + continue; + } + + v2 = sg_dma_address(sg); + mptscsih_add_sge(psge, sgflags | thisxfer, v2); + + sg++; /* Get next SG element from the OS */ + psge += (sizeof(u32) + sizeof(dma_addr_t)); + sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); + sg_done++; + } + + if (numSgeThisFrame == sges_left) { + /* Add last element, end of buffer and end of list flags. + */ + sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT | + MPT_SGE_FLAGS_END_OF_BUFFER | + MPT_SGE_FLAGS_END_OF_LIST; + + /* Add last SGE and set termination flags. + * Note: Last SGE may have a length of 0 - which should be ok. + */ + thisxfer = sg_dma_len(sg); + + v2 = sg_dma_address(sg); + mptscsih_add_sge(psge, sgflags | thisxfer, v2); + /* + sg++; + psge += (sizeof(u32) + sizeof(dma_addr_t)); + */ + sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); + sg_done++; + + if (chainSge) { + /* The current buffer is a chain buffer, + * but there is not another one. + * Update the chain element + * Offset and Length fields. + */ + mptscsih_add_chain((char *)chainSge, 0, sgeOffset, hd->ChainBufferDMA + chain_dma_off); + } else { + /* The current buffer is the original MF + * and there is no Chain buffer. + */ + pReq->ChainOffset = 0; + } + } else { + /* At least one chain buffer is needed. + * Complete the first MF + * - last SGE element, set the LastElement bit + * - set ChainOffset (words) for orig MF + * (OR finish previous MF chain buffer) + * - update MFStructPtr ChainIndex + * - Populate chain element + * Also + * Loop until done. + */ + + dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %d\n", + hd->ioc->name, sg_done)); + + /* Set LAST_ELEMENT flag for last non-chain element + * in the buffer. Since psge points at the NEXT + * SGE element, go back one SGE element, update the flags + * and reset the pointer. (Note: sgflags & thisxfer are already + * set properly). + */ + if (sg_done) { + u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t))); + sgflags = le32_to_cpu(*ptmp); + sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT; + *ptmp = cpu_to_le32(sgflags); + } + + if (chainSge) { + /* The current buffer is a chain buffer. + * chainSge points to the previous Chain Element. + * Update its chain element Offset and Length (must + * include chain element size) fields. + * Old chain element is now complete. + */ + u8 nextChain = (u8) (sgeOffset >> 2); + sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); + mptscsih_add_chain((char *)chainSge, nextChain, sgeOffset, hd->ChainBufferDMA + chain_dma_off); + } else { + /* The original MF buffer requires a chain buffer - + * set the offset. + * Last element in this MF is a chain element. + */ + pReq->ChainOffset = (u8) (sgeOffset >> 2); + } + + sges_left -= sg_done; + + + /* NOTE: psge points to the beginning of the chain element + * in current buffer. Get a chain buffer. + */ + if ((mptscsih_getFreeChainBuffer(hd, &newIndex)) == FAILED) + return FAILED; + + /* Update the tracking arrays. + * If chainSge == NULL, update ReqToChain, else ChainToChain + */ + if (chainSge) { + hd->ChainToChain[chain_idx] = newIndex; + } else { + hd->ReqToChain[req_idx] = newIndex; + } + chain_idx = newIndex; + chain_dma_off = hd->ioc->req_sz * chain_idx; + + /* Populate the chainSGE for the current buffer. + * - Set chain buffer pointer to psge and fill + * out the Address and Flags fields. + */ + chainSge = (char *) psge; + dsgprintk((KERN_INFO " Current buff @ %p (index 0x%x)", + psge, req_idx)); + + /* Start the SGE for the next buffer + */ + psge = (char *) (hd->ChainBuffer + chain_dma_off); + sgeOffset = 0; + sg_done = 0; + + dsgprintk((KERN_INFO " Chain buff @ %p (index 0x%x)\n", + psge, chain_idx)); + + /* Start the SGE for the next buffer + */ + + goto nextSGEset; + } + + return SUCCESS; +} /* mptscsih_AddSGE() */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_io_done - Main SCSI IO callback routine registered to + * Fusion MPT (base) driver + * @ioc: Pointer to MPT_ADAPTER structure + * @mf: Pointer to original MPT request frame + * @r: Pointer to MPT reply frame (NULL if TurboReply) + * + * This routine is called from mpt.c::mpt_interrupt() at the completion + * of any SCSI IO request. + * This routine is registered with the Fusion MPT (base) driver at driver + * load/init time via the mpt_register() API call. + * + * Returns 1 indicating alloc'd request frame ptr should be freed. + */ +static int +mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) +{ + Scsi_Cmnd *sc; + MPT_SCSI_HOST *hd; + SCSIIORequest_t *pScsiReq; + SCSIIOReply_t *pScsiReply; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0) + unsigned long flags; +#endif + u16 req_idx; + + hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; + + if ((mf == NULL) || + (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) { + printk(MYIOC_s_ERR_FMT "%s req frame ptr! (=%p)!\n", + ioc->name, mf?"BAD":"NULL", (void *) mf); + return 0; + } + + req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); + sc = hd->ScsiLookup[req_idx]; + if (sc == NULL) { + MPIHeader_t *hdr = (MPIHeader_t *)mf; + + atomic_dec(&queue_depth); + + /* Remark: writeSDP1 will use the ScsiDoneCtx + * If a SCSI I/O cmd, device disabled by OS and + * completion done. Cannot touch sc struct. Just free mem. + */ + if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST) + printk(MYIOC_s_ERR_FMT "NULL ScsiCmd ptr!\n", ioc->name); + + mptscsih_freeChainBuffers(hd, req_idx); + return 1; + } + + dmfprintk((MYIOC_s_INFO_FMT "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d)\n", + ioc->name, mf, mr, sc, req_idx)); + + atomic_dec(&queue_depth); + + sc->result = DID_OK << 16; /* Set default reply as OK */ + pScsiReq = (SCSIIORequest_t *) mf; + pScsiReply = (SCSIIOReply_t *) mr; + +#ifdef MPTSCSIH_DBG_TIMEOUT + if (ioc->timeout_cnt > 0) { + int ii, left = 0; + + for (ii=0; ii < 8; ii++) { + if (sc == foo_to[ii]) { + printk(MYIOC_s_INFO_FMT "complete (%p, %ld)\n", + ioc->name, sc, jiffies); + foo_to[ii] = NULL; + } + if (foo_to[ii] != NULL) + left++; + } + + if (left == 0) { + ioc->timeout_maxcnt = 0; + ioc->timeout_cnt = 0; + } + } +#endif + if (pScsiReply == NULL) { + /* special context reply handling */ + + /* If regular Inquiry cmd - save inquiry data + */ + if (pScsiReq->CDB[0] == INQUIRY && !(pScsiReq->CDB[1] & 0x3)) { + int dlen; + + dlen = le32_to_cpu(pScsiReq->DataLength); + if (dlen >= SCSI_STD_INQUIRY_BYTES) { + mptscsih_initTarget(hd, + sc->channel, + sc->target, + pScsiReq->LUN[1], + sc->buffer, + dlen); + } + } + } else { + u32 xfer_cnt; + u16 status; + u8 scsi_state; + + status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK; + scsi_state = pScsiReply->SCSIState; + + dprintk((KERN_NOTICE " Uh-Oh! (%d:%d:%d) mf=%p, mr=%p, sc=%p\n", + ioc->id, pScsiReq->TargetID, pScsiReq->LUN[1], + mf, mr, sc)); + dprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh" + ", SCSIStatus=%02xh, IOCLogInfo=%08xh\n", + status, scsi_state, pScsiReply->SCSIStatus, + le32_to_cpu(pScsiReply->IOCLogInfo))); + + if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) + copy_sense_data(sc, hd, mf, pScsiReply); + + /* + * Look for + dump FCP ResponseInfo[]! + */ + if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID) { + dprintk((KERN_NOTICE " FCP_ResponseInfo=%08xh\n", + le32_to_cpu(pScsiReply->ResponseInfo))); + } + + switch(status) { + case MPI_IOCSTATUS_BUSY: /* 0x0002 */ + /* CHECKME! + * Maybe: DRIVER_BUSY | SUGGEST_RETRY | DID_SOFT_ERROR (retry) + * But not: DID_BUS_BUSY lest one risk + * killing interrupt handler:-( + */ + sc->result = STS_BUSY; + break; + + case MPI_IOCSTATUS_SCSI_INVALID_BUS: /* 0x0041 */ + case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: /* 0x0042 */ + sc->result = DID_BAD_TARGET << 16; + break; + + case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */ + /* Spoof to SCSI Selection Timeout! */ + sc->result = DID_NO_CONNECT << 16; + + if (hd->sel_timeout[pScsiReq->TargetID] < 0xFFFF) + hd->sel_timeout[pScsiReq->TargetID]++; + break; + + case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */ +#ifndef MPT_SCSI_USE_NEW_EH + search_taskQ_for_cmd(sc, hd); +#endif + /* Linux handles an unsolicited DID_RESET better + * than an unsolicited DID_ABORT. + */ + sc->result = DID_RESET << 16; + + /* GEM Workaround. */ + if (hd->is_spi) + mptscsih_no_negotiate(hd, sc->target); + break; + + case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */ + case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */ +#ifndef MPT_SCSI_USE_NEW_EH + search_taskQ_for_cmd(sc, hd); +#endif + sc->result = DID_RESET << 16; + + /* GEM Workaround. */ + if (hd->is_spi) + mptscsih_no_negotiate(hd, sc->target); + break; + + case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */ + case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */ + /* + * YIKES! I just discovered that SCSI IO which + * returns check condition, SenseKey=05 (ILLEGAL REQUEST) + * and ASC/ASCQ=94/01 (LSI Logic RAID vendor specific), + * comes down this path! + * Do upfront check for valid SenseData and give it + * precedence! + */ + sc->result = (DID_OK << 16) | pScsiReply->SCSIStatus; + if (scsi_state == 0) { + ; + } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) { + /* Have already saved the status and sense data + */ + ; + } else if (scsi_state & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) { + /* What to do? + */ + sc->result = DID_SOFT_ERROR << 16; + } + else if (scsi_state & MPI_SCSI_STATE_TERMINATED) { + /* Not real sure here either... */ + sc->result = DID_RESET << 16; + } + + /* Give report and update residual count. + */ + xfer_cnt = le32_to_cpu(pScsiReply->TransferCount); + dprintk((KERN_NOTICE " sc->underflow={report ERR if < %02xh bytes xfer'd}\n", + sc->underflow)); + dprintk((KERN_NOTICE " ActBytesXferd=%02xh\n", xfer_cnt)); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) + sc->resid = sc->request_bufflen - xfer_cnt; + dprintk((KERN_NOTICE " SET sc->resid=%02xh\n", sc->resid)); +#endif + + /* Report Queue Full + */ + if (sc->result == MPI_SCSI_STATUS_TASK_SET_FULL) + mptscsih_report_queue_full(sc, pScsiReply, pScsiReq); + + /* If regular Inquiry cmd and some data was transferred, + * save inquiry data + */ + if ( pScsiReq->CDB[0] == INQUIRY + && !(pScsiReq->CDB[1] & 0x3) + && xfer_cnt >= SCSI_STD_INQUIRY_BYTES + ) { + mptscsih_initTarget(hd, + sc->channel, + sc->target, + pScsiReq->LUN[1], + sc->buffer, + xfer_cnt); + } + break; + + case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */ + case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */ + sc->result = (DID_OK << 16) | pScsiReply->SCSIStatus; + if (scsi_state == 0) { + ; + } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) { + /* + * If running against circa 200003dd 909 MPT f/w, + * may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL + * (QUEUE_FULL) returned from device! --> get 0x0000?128 + * and with SenseBytes set to 0. + */ + if (pScsiReply->SCSIStatus == MPI_SCSI_STATUS_TASK_SET_FULL) + mptscsih_report_queue_full(sc, pScsiReply, pScsiReq); + +#ifndef MPT_SCSI_USE_NEW_EH + /* ADDED 20011120 -sralston + * Scsi mid-layer (old_eh) doesn't seem to like it + * when RAID returns SCSIStatus=02 (CHECK CONDITION), + * SenseKey=01 (RECOVERED ERROR), ASC/ASCQ=95/01. + * Seems to be * treating this as a IO error:-( + * + * So just lie about it altogether here. + * + * NOTE: It still gets reported to syslog via + * mpt_ScsiHost_ErrorReport from copy_sense_data + * call far above. + */ + if ( pScsiReply->SCSIStatus == STS_CHECK_CONDITION + && SD_Sense_Key(sc->sense_buffer) == SK_RECOVERED_ERROR + ) { + sc->result = 0; + } +#endif + + } + else if (scsi_state & + (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS) + ) { + /* + * What to do? + */ + sc->result = DID_SOFT_ERROR << 16; + } + else if (scsi_state & MPI_SCSI_STATE_TERMINATED) { + /* Not real sure here either... */ + sc->result = DID_RESET << 16; + } + else if (scsi_state & MPI_SCSI_STATE_QUEUE_TAG_REJECTED) { + /* Device Inq. data indicates that it supports + * QTags, but rejects QTag messages. + * This command completed OK. + * + * Not real sure here either so do nothing... */ + } + + if (sc->result == MPI_SCSI_STATUS_TASK_SET_FULL) + mptscsih_report_queue_full(sc, pScsiReply, pScsiReq); + + /* Add handling of: + * Reservation Conflict, Busy, + * Command Terminated, CHECK + */ + + /* If regular Inquiry cmd - save inquiry data + */ + xfer_cnt = le32_to_cpu(pScsiReply->TransferCount); + if ( sc->result == (DID_OK << 16) + && pScsiReq->CDB[0] == INQUIRY + && !(pScsiReq->CDB[1] & 0x3) + && xfer_cnt >= SCSI_STD_INQUIRY_BYTES + ) { + mptscsih_initTarget(hd, + sc->channel, + sc->target, + pScsiReq->LUN[1], + sc->buffer, + xfer_cnt); + } + break; + + case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */ + if (pScsiReply->SCSIState & MPI_SCSI_STATE_TERMINATED) { + /* Not real sure here either... */ + sc->result = DID_RESET << 16; + } else + sc->result = DID_SOFT_ERROR << 16; + break; + + case MPI_IOCSTATUS_INVALID_FUNCTION: /* 0x0001 */ + case MPI_IOCSTATUS_INVALID_SGL: /* 0x0003 */ + case MPI_IOCSTATUS_INTERNAL_ERROR: /* 0x0004 */ + case MPI_IOCSTATUS_RESERVED: /* 0x0005 */ + case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: /* 0x0006 */ + case MPI_IOCSTATUS_INVALID_FIELD: /* 0x0007 */ + case MPI_IOCSTATUS_INVALID_STATE: /* 0x0008 */ + case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: /* 0x0044 */ + case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */ + case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED: /* 0x004A */ + default: + /* + * What to do? + */ + sc->result = DID_SOFT_ERROR << 16; + break; + + } /* switch(status) */ + + dprintk((KERN_NOTICE " sc->result set to %08xh\n", sc->result)); + } /* end of address reply case */ + + /* Unmap the DMA buffers, if any. */ + if (sc->use_sg) { + pci_unmap_sg(ioc->pcidev, (struct scatterlist *) sc->request_buffer, + sc->use_sg, scsi_to_pci_dma_dir(sc->sc_data_direction)); + } else if (sc->request_bufflen) { + scPrivate *my_priv; + + my_priv = (scPrivate *) &sc->SCp; + pci_unmap_single(ioc->pcidev, (dma_addr_t)(ulong)my_priv->p1, + sc->request_bufflen, + scsi_to_pci_dma_dir(sc->sc_data_direction)); + } + + hd->ScsiLookup[req_idx] = NULL; + +#ifndef MPT_SCSI_USE_NEW_EH + sc->host_scribble = NULL; +#endif + + MPT_HOST_LOCK(flags); + sc->scsi_done(sc); /* Issue the command callback */ + MPT_HOST_UNLOCK(flags); + + /* Free Chain buffers */ + mptscsih_freeChainBuffers(hd, req_idx); + + return 1; +} + +#ifndef MPT_SCSI_USE_NEW_EH /* { */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * search_taskQ - Search SCSI task mgmt request queue for specific + * request type. + * @remove: (Boolean) Should request be removed if found? + * @sc: Pointer to Scsi_Cmnd structure + * @task_type: Task type to search for + * + * Returns pointer to MPT request frame if found, or %NULL if request + * was not found. + */ +static MPT_FRAME_HDR * +search_taskQ(int remove, Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, u8 task_type) +{ + MPT_FRAME_HDR *mf = NULL; + unsigned long flags; + int count = 0; + int list_sz; + + dprintk((KERN_INFO MYNAM ": search_taskQ(%d,sc=%p,%d) called\n", + remove, sc, task_type)); + spin_lock_irqsave(&hd->ioc->FreeQlock, flags); + list_sz = hd->taskQcnt; + if (! Q_IS_EMPTY(&hd->taskQ)) { + mf = hd->taskQ.head; + do { + count++; + if (mf->u.frame.linkage.argp1 == sc && + mf->u.frame.linkage.arg1 == task_type) { + if (remove) { + Q_DEL_ITEM(&mf->u.frame.linkage); + hd->taskQcnt--; + atomic_dec(&mpt_taskQdepth); + + /* Don't save mf into nextmf because + * exit after command has been deleted. + */ + + /* Place the MF back on the FreeQ */ + Q_ADD_TAIL(&hd->ioc->FreeQ, + &mf->u.frame.linkage, + MPT_FRAME_HDR); +#ifdef MFCNT + hd->ioc->mfcnt--; +#endif + } + break; + } + } while ((mf = mf->u.frame.linkage.forw) != (MPT_FRAME_HDR*)&hd->taskQ); + if (mf == (MPT_FRAME_HDR*)&hd->taskQ) { + mf = NULL; + } + } + spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); + + if (list_sz) { + dprintk((KERN_INFO " Results=%p (%sFOUND%s)!\n", + mf, + mf ? "" : "NOT_", + (mf && remove) ? "+REMOVED" : "" )); + dprintk((KERN_INFO " (searched thru %d of %d items on taskQ)\n", + count, + list_sz )); + } + + return mf; +} + +/* + * clean_taskQ - Clean the SCSI task mgmt request for + * this SCSI host instance. + * @hd: MPT_SCSI_HOST pointer + * + * Returns: None. + */ +static void +clean_taskQ(MPT_SCSI_HOST *hd) +{ + MPT_FRAME_HDR *mf = NULL; + MPT_FRAME_HDR *nextmf = NULL; + MPT_ADAPTER *ioc = hd->ioc; + unsigned long flags; + + dprintk((KERN_INFO MYNAM ": clean_taskQ called\n")); + + spin_lock_irqsave(&ioc->FreeQlock, flags); + if (! Q_IS_EMPTY(&hd->taskQ)) { + mf = hd->taskQ.head; + do { + Q_DEL_ITEM(&mf->u.frame.linkage); + hd->taskQcnt--; + atomic_dec(&mpt_taskQdepth); + + nextmf = mf->u.frame.linkage.forw; + + /* Place the MF back on the FreeQ */ + Q_ADD_TAIL(&ioc->FreeQ, &mf->u.frame.linkage, + MPT_FRAME_HDR); +#ifdef MFCNT + hd->ioc->mfcnt--; +#endif + } while ((mf = nextmf) != (MPT_FRAME_HDR*)&hd->taskQ); + } + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + + return; +} + +/* + * search_taskQ_for_cmd - Search the SCSI task mgmt request queue for + * the specified command. If found, delete + * @hd: MPT_SCSI_HOST pointer + * + * Returns: None. + */ +static void +search_taskQ_for_cmd(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd) +{ + MPT_FRAME_HDR *mf = NULL; + unsigned long flags; + int count = 0; + + dprintk((KERN_INFO MYNAM ": search_taskQ_for_cmd(sc=%p) called\n", sc)); + spin_lock_irqsave(&hd->ioc->FreeQlock, flags); + if (! Q_IS_EMPTY(&hd->taskQ)) { + mf = hd->taskQ.head; + do { + count++; + if (mf->u.frame.linkage.argp1 == sc) { + Q_DEL_ITEM(&mf->u.frame.linkage); + hd->taskQcnt--; + atomic_dec(&mpt_taskQdepth); + dprintk((KERN_INFO MYNAM + ": Cmd %p found! Deleting.\n", sc)); + + /* Don't save mf into nextmf because + * exit after command has been deleted. + */ + + /* Place the MF back on the FreeQ */ + Q_ADD_TAIL(&hd->ioc->FreeQ, + &mf->u.frame.linkage, + MPT_FRAME_HDR); +#ifdef MFCNT + hd->ioc->mfcnt--; +#endif + break; + } + } while ((mf = mf->u.frame.linkage.forw) != (MPT_FRAME_HDR*)&hd->taskQ); + } + spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); + + return; +} + +#endif /* } MPT_SCSI_USE_NEW_EH */ + + +/* + * Flush all commands on the doneQ. + * Lock Q when deleting/adding members + * Lock io_request_lock for OS callback. + */ +static void +flush_doneQ(MPT_SCSI_HOST *hd) +{ + MPT_DONE_Q *buffer; + Scsi_Cmnd *SCpnt; + unsigned long flags; + + /* Flush the doneQ. + */ + dtmprintk((KERN_INFO MYNAM ": flush_doneQ called\n")); + while (1) { + spin_lock_irqsave(&hd->freedoneQlock, flags); + if (Q_IS_EMPTY(&hd->doneQ)) { + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + break; + } + + buffer = hd->doneQ.head; + /* Delete from Q + */ + Q_DEL_ITEM(buffer); + + /* Set the Scsi_Cmnd pointer + */ + SCpnt = (Scsi_Cmnd *) buffer->argp; + buffer->argp = NULL; + + /* Add to the freeQ + */ + Q_ADD_TAIL(&hd->freeQ.head, buffer, MPT_DONE_Q); + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + + /* Do the OS callback. + */ + MPT_HOST_LOCK(flags); + SCpnt->scsi_done(SCpnt); + MPT_HOST_UNLOCK(flags); + } + + return; +} + +/* + * Search the doneQ for a specific command. If found, delete from Q. + * Calling function will finish processing. + */ +static void +search_doneQ_for_cmd(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt) +{ + unsigned long flags; + MPT_DONE_Q *buffer; + + spin_lock_irqsave(&hd->freedoneQlock, flags); + if (!Q_IS_EMPTY(&hd->doneQ)) { + buffer = hd->doneQ.head; + do { + Scsi_Cmnd *sc = (Scsi_Cmnd *) buffer->argp; + if (SCpnt == sc) { + Q_DEL_ITEM(buffer); + SCpnt->result = sc->result; + + /* Set the Scsi_Cmnd pointer + */ + buffer->argp = NULL; + + /* Add to the freeQ + */ + Q_ADD_TAIL(&hd->freeQ.head, buffer, MPT_DONE_Q); + break; + } + } while ((buffer = buffer->forw) != (MPT_DONE_Q *) &hd->doneQ); + } + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + return; +} + +static void +mptscsih_reset_timeouts (MPT_SCSI_HOST *hd) +{ + Scsi_Cmnd *SCpnt = NULL; + int ii; + int max = hd->ioc->req_depth; + + for (ii= 0; ii < max; ii++) { + if ((SCpnt = hd->ScsiLookup[ii]) != NULL) { + mod_timer(&SCpnt->eh_timeout, jiffies + (HZ * 60)); + dtmprintk((MYIOC_s_WARN_FMT "resetting SCpnt=%p timeout + 60HZ", + (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt)); + } + } +} + +/* + * mptscsih_flush_running_cmds - For each command found, search + * Scsi_Host instance taskQ and reply to OS. + * Called only if recovering from a FW reload. + * @hd: Pointer to a SCSI HOST structure + * + * Returns: None. + * + * Must be called while new I/Os are being queued. + */ +static void +mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd) +{ + Scsi_Cmnd *SCpnt = NULL; + MPT_FRAME_HDR *mf = NULL; + MPT_DONE_Q *buffer = NULL; + int ii; + int max = hd->ioc->req_depth; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0) + unsigned long flags; +#endif + + dprintk((KERN_INFO MYNAM ": flush_ScsiLookup called\n")); + for (ii= 0; ii < max; ii++) { + if ((SCpnt = hd->ScsiLookup[ii]) != NULL) { + + /* Command found. + */ +#ifndef MPT_SCSI_USE_NEW_EH + /* Search taskQ, if found, delete. + */ + search_taskQ_for_cmd(SCpnt, hd); +#endif + + /* Search pendingQ, if found, + * delete from Q. If found, do not decrement + * queue_depth, command never posted. + */ + if (mptscsih_search_pendingQ(hd, ii) == NULL) + atomic_dec(&queue_depth); + + /* Null ScsiLookup index + */ + hd->ScsiLookup[ii] = NULL; + + mf = MPT_INDEX_2_MFPTR(hd->ioc, ii); + dmfprintk(( "flush: ScsiDone (mf=%p,sc=%p)\n", mf, SCpnt)); + + /* Set status, free OS resources (SG DMA buffers) + * Free driver resources (chain, msg buffers) + */ + if (SCpnt->use_sg) { + pci_unmap_sg(hd->ioc->pcidev, (struct scatterlist *) SCpnt->request_buffer, + SCpnt->use_sg, scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); + } else if (SCpnt->request_bufflen) { + scPrivate *my_priv; + + my_priv = (scPrivate *) &SCpnt->SCp; + pci_unmap_single(hd->ioc->pcidev, (dma_addr_t)(ulong)my_priv->p1, + SCpnt->request_bufflen, + scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); + } + SCpnt->result = DID_RESET << 16; + SCpnt->host_scribble = NULL; + + /* Free Chain buffers */ + mptscsih_freeChainBuffers(hd, ii); + + /* Free Message frames */ + mpt_free_msg_frame(ScsiDoneCtx, hd->ioc->id, mf); + +#if 1 + /* Post to doneQ, do not reply until POST phase + * of reset handler....prevents new commands from + * being queued. + */ + spin_lock_irqsave(&hd->freedoneQlock, flags); + if (!Q_IS_EMPTY(&hd->freeQ)) { + buffer = hd->freeQ.head; + Q_DEL_ITEM(buffer); + + /* Set the Scsi_Cmnd pointer + */ + buffer->argp = (void *)SCpnt; + + /* Add to the doneQ + */ + Q_ADD_TAIL(&hd->doneQ.head, buffer, MPT_DONE_Q); + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + } else { + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + SCpnt->scsi_done(SCpnt); + } +#else + MPT_HOST_LOCK(flags); + SCpnt->scsi_done(SCpnt); /* Issue the command callback */ + MPT_HOST_UNLOCK(flags); +#endif + } + } + + return; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_initChainBuffers - Allocate memory for and initialize + * chain buffers, chain buffer control arrays and spinlock. + * @hd: Pointer to MPT_SCSI_HOST structure + * @init: If set, initialize the spin lock. + */ +static int +mptscsih_initChainBuffers (MPT_SCSI_HOST *hd, int init) +{ + MPT_FRAME_HDR *chain; + u8 *mem; + unsigned long flags; + int sz, ii, num_chain; + int scale, num_sge; + + /* ReqToChain size must equal the req_depth + * index = req_idx + */ + sz = hd->ioc->req_depth * sizeof(int); + if (hd->ReqToChain == NULL) { + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) + return -1; + + hd->ReqToChain = (int *) mem; + } else { + mem = (u8 *) hd->ReqToChain; + } + memset(mem, 0xFF, sz); + + + /* ChainToChain size must equal the total number + * of chain buffers to be allocated. + * index = chain_idx + * + * Calculate the number of chain buffers needed(plus 1) per I/O + * then multiply the the maximum number of simultaneous cmds + * + * num_sge = num sge in request frame + last chain buffer + * scale = num sge per chain buffer if no chain element + */ + scale = hd->ioc->req_sz/(sizeof(dma_addr_t) + sizeof(u32)); + if (sizeof(dma_addr_t) == sizeof(u64)) + num_sge = scale + (hd->ioc->req_sz - 60) / (sizeof(dma_addr_t) + sizeof(u32)); + else + num_sge = 1+ scale + (hd->ioc->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32)); + + num_chain = 1; + while (hd->max_sge - num_sge > 0) { + num_chain++; + num_sge += (scale - 1); + } + num_chain++; + + if ((int) hd->ioc->chip_type > (int) FC929) + num_chain *= MPT_SCSI_CAN_QUEUE; + else + num_chain *= MPT_FC_CAN_QUEUE; + + hd->num_chain = num_chain; + + sz = num_chain * sizeof(int); + if (hd->ChainToChain == NULL) { + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) + return -1; + + hd->ChainToChain = (int *) mem; + } else { + mem = (u8 *) hd->ChainToChain; + } + memset(mem, 0xFF, sz); + + sz = num_chain * hd->ioc->req_sz; + if (hd->ChainBuffer == NULL) { + /* Allocate free chain buffer pool + */ + mem = pci_alloc_consistent(hd->ioc->pcidev, sz, &hd->ChainBufferDMA); + if (mem == NULL) + return -1; + + hd->ChainBuffer = (u8*)mem; + } else { + mem = (u8 *) hd->ChainBuffer; + } + memset(mem, 0, sz); + + dprintk((KERN_INFO " ChainBuffer @ %p(%p), sz=%d\n", + hd->ChainBuffer, (void *)(ulong)hd->ChainBufferDMA, sz)); + + /* Initialize the free chain Q. + */ + if (init) { + spin_lock_init(&hd->FreeChainQlock); + } + + spin_lock_irqsave (&hd->FreeChainQlock, flags); + Q_INIT(&hd->FreeChainQ, MPT_FRAME_HDR); + + /* Post the chain buffers to the FreeChainQ. + */ + mem = (u8 *)hd->ChainBuffer; + for (ii=0; ii < num_chain; ii++) { + chain = (MPT_FRAME_HDR *) mem; + Q_ADD_TAIL(&hd->FreeChainQ.head, &chain->u.frame.linkage, MPT_FRAME_HDR); + mem += hd->ioc->req_sz; + } + spin_unlock_irqrestore(&hd->FreeChainQlock, flags); + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Hack! It might be nice to report if a device is returning QUEUE_FULL + * but maybe not each and every time... + */ +static long last_queue_full = 0; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_report_queue_full - Report QUEUE_FULL status returned + * from a SCSI target device. + * @sc: Pointer to Scsi_Cmnd structure + * @pScsiReply: Pointer to SCSIIOReply_t + * @pScsiReq: Pointer to original SCSI request + * + * This routine periodically reports QUEUE_FULL status returned from a + * SCSI target device. It reports this to the console via kernel + * printk() API call, not more than once every 10 seconds. + */ +static void +mptscsih_report_queue_full(Scsi_Cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq) +{ + long time = jiffies; + + if (time - last_queue_full > 10 * HZ) { + char *ioc_str = "ioc?"; + + if (sc->host != NULL && sc->host->hostdata != NULL) + ioc_str = ((MPT_SCSI_HOST *)sc->host->hostdata)->ioc->name; + printk(MYIOC_s_WARN_FMT "Device (%d:%d:%d) reported QUEUE_FULL!\n", + ioc_str, 0, sc->target, sc->lun); + last_queue_full = time; + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int BeenHereDoneThat = 0; +static char *info_kbuf = NULL; + +/* SCSI host fops start here... */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_detect - Register MPT adapter(s) as SCSI host(s) with + * linux scsi mid-layer. + * @tpnt: Pointer to Scsi_Host_Template structure + * + * (linux Scsi_Host_Template.detect routine) + * + * Returns number of SCSI host adapters that were successfully + * registered with the linux scsi mid-layer via the scsi_register() + * API call. + */ +int +mptscsih_detect(Scsi_Host_Template *tpnt) +{ + struct Scsi_Host *sh = NULL; + MPT_SCSI_HOST *hd = NULL; + MPT_ADAPTER *this; + MPT_DONE_Q *freedoneQ; + unsigned long flags; + int sz, ii; + int numSGE = 0; + int scale; + int ioc_cap; + u8 *mem; + + if (! BeenHereDoneThat++) { + /*show_mptmod_ver(my_NAME, my_VERSION);*/ + + ScsiDoneCtx = mpt_register(mptscsih_io_done, MPTSCSIH_DRIVER); + ScsiTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSCSIH_DRIVER); + ScsiScanDvCtx = mpt_register(mptscsih_scandv_complete, MPTSCSIH_DRIVER); + +#ifndef MPT_SCSI_USE_NEW_EH + spin_lock_init(&mytaskQ_lock); +#endif + + if (mpt_event_register(ScsiDoneCtx, mptscsih_event_process) == 0) { + dprintk((KERN_INFO MYNAM ": Registered for IOC event notifications\n")); + } else { + /* FIXME! */ + } + + if (mpt_reset_register(ScsiDoneCtx, mptscsih_ioc_reset) == 0) { + dprintk((KERN_INFO MYNAM ": Registered for IOC reset notifications\n")); + } else { + /* FIXME! */ + } + } + dprintk((KERN_INFO MYNAM ": mptscsih_detect()\n")); + +#ifdef MODULE + /* Evaluate the command line arguments, if any */ + if (mptscsih) + mptscsih_setup(mptscsih); +#endif +#ifndef MPT_SCSI_USE_NEW_EH + atomic_set(&mpt_taskQdepth, 0); +#endif + + this = mpt_adapter_find_first(); + while (this != NULL) { + /* 20010202 -sralston + * Added sanity check on readiness of the MPT adapter. + */ + if (this->last_state != MPI_IOC_STATE_OPERATIONAL) { + printk(MYIOC_s_WARN_FMT "Skipping because it's not operational!\n", + this->name); + continue; + } + + if (!this->active) { + printk(MYIOC_s_WARN_FMT "Skipping because it's disabled!\n", + this->name); + continue; + } + + + /* Sanity check - ensure at least 1 port is INITIATOR capable + */ + ioc_cap = 0; + for (ii=0; ii < this->facts.NumberOfPorts; ii++) { + if (this->pfacts[ii].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_INITIATOR) + ioc_cap ++; + } + + if (!ioc_cap) { + printk(MYIOC_s_WARN_FMT "Skipping because SCSI Initiator mode is NOT enabled!\n", + this->name); + continue; + } + +/* SAE: No proc! */ +#if defined(CONFIG_PROC_FS) + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + tpnt->proc_dir = &proc_mpt_scsihost; +#endif + tpnt->proc_info = mptscsih_proc_info; +#endif /* SAE: CONFIG_PROC_FS */ + sh = scsi_register(tpnt, sizeof(MPT_SCSI_HOST)); + if (sh != NULL) { + spin_lock_irqsave(&this->FreeQlock, flags); + sh->io_port = 0; + sh->n_io_port = 0; + sh->irq = 0; + + /* Yikes! This is important! + * Otherwise, by default, linux + * only scans target IDs 0-7! + * pfactsN->MaxDevices unreliable + * (not supported in early + * versions of the FW). + * max_id = 1 + actual max id, + * max_lun = 1 + actual last lun, + * see hosts.h :o( + */ + if ((int)this->chip_type > (int)FC929) + sh->max_id = MPT_MAX_SCSI_DEVICES; + else { + /* For FC, increase the queue depth + * from MPT_SCSI_CAN_QUEUE (31) + * to MPT_FC_CAN_QUEUE (63). + */ + sh->can_queue = MPT_FC_CAN_QUEUE; + sh->max_id = MPT_MAX_FC_DEVICES<256 ? MPT_MAX_FC_DEVICES : 255; + } + sh->max_lun = MPT_LAST_LUN + 1; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,7) + sh->max_sectors = MPT_SCSI_MAX_SECTORS; +#endif +/* SAE: The logic of this one just baffles me, should be && defined (I think)... */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,20) && defined CONFIG_HIGHIO + sh->highmem_io = 1; +#endif + /* MPI uses {port, bus, id, lun}, but logically maps + * devices on different ports to different buses, i.e., + * bus 1 may be the 2nd bus on port 0 or the 1st bus on port 1. + * Map bus to channel, ignore port number in SCSI.... + * hd->port = 0; + * If max_channel > 0, need to adjust mem alloc, free, DV + * and all access to VirtDev + */ + sh->max_channel = 0; + sh->this_id = this->pfacts[0].PortSCSIID; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,44) + /* OS entry to allow host drivers to force + * a queue depth on a per device basis. + */ + sh->select_queue_depths = mptscsih_select_queue_depths; +#endif + /* Required entry. + */ + sh->unique_id = this->id; + + /* Verify that we won't exceed the maximum + * number of chain buffers + * We can optimize: ZZ = req_sz/sizeof(SGE) + * For 32bit SGE's: + * numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ + * + (req_sz - 64)/sizeof(SGE) + * A slightly different algorithm is required for + * 64bit SGEs. + */ + scale = this->req_sz/(sizeof(dma_addr_t) + sizeof(u32)); + if (sizeof(dma_addr_t) == sizeof(u64)) { + numSGE = (scale - 1) * (this->facts.MaxChainDepth-1) + scale + + (this->req_sz - 60) / (sizeof(dma_addr_t) + sizeof(u32)); + } else { + numSGE = 1 + (scale - 1) * (this->facts.MaxChainDepth-1) + scale + + (this->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32)); + } + + if (numSGE < sh->sg_tablesize) { + /* Reset this value */ + dprintk((MYIOC_s_INFO_FMT + "Resetting sg_tablesize to %d from %d\n", + this->name, numSGE, sh->sg_tablesize)); + sh->sg_tablesize = numSGE; + } + + /* Set the pci device pointer in Scsi_Host structure. + */ + scsi_set_pci_device(sh, this->pcidev); + + spin_unlock_irqrestore(&this->FreeQlock, flags); + + hd = (MPT_SCSI_HOST *) sh->hostdata; + hd->ioc = this; + hd->max_sge = sh->sg_tablesize; + + if ((int)this->chip_type > (int)FC929) + hd->is_spi = 1; + + if (DmpService && + (this->chip_type == FC919 || this->chip_type == FC929)) + hd->is_multipath = 1; + + /* SCSI needs Scsi_Cmnd lookup table! + * (with size equal to req_depth*PtrSz!) + */ + sz = hd->ioc->req_depth * sizeof(void *); + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) + goto done; + + memset(mem, 0, sz); + hd->ScsiLookup = (struct scsi_cmnd **) mem; + + dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p, sz=%d\n", + this->name, hd->ScsiLookup, sz)); + + if (mptscsih_initChainBuffers(hd, 1) < 0) + goto done; + + /* Allocate memory for free and doneQ's + */ + sz = sh->can_queue * sizeof(MPT_DONE_Q); + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) + goto done; + + memset(mem, 0xFF, sz); + hd->memQ = mem; + + /* Initialize the free, done and pending Qs. + */ + Q_INIT(&hd->freeQ, MPT_DONE_Q); + Q_INIT(&hd->doneQ, MPT_DONE_Q); + Q_INIT(&hd->pendingQ, MPT_DONE_Q); + spin_lock_init(&hd->freedoneQlock); + + mem = hd->memQ; + for (ii=0; ii < sh->can_queue; ii++) { + freedoneQ = (MPT_DONE_Q *) mem; + Q_ADD_TAIL(&hd->freeQ.head, freedoneQ, MPT_DONE_Q); + mem += sizeof(MPT_DONE_Q); + } + + /* Initialize this Scsi_Host + * internal task Q. + */ + Q_INIT(&hd->taskQ, MPT_FRAME_HDR); + hd->taskQcnt = 0; + + /* Allocate memory for the device structures. + * A non-Null pointer at an offset + * indicates a device exists. + * max_id = 1 + maximum id (hosts.h) + */ + sz = sh->max_id * sizeof(void *); + mem = kmalloc(sz, GFP_ATOMIC); + if (mem == NULL) + goto done; + + memset(mem, 0, sz); + hd->Targets = (VirtDevice **) mem; + + dprintk((KERN_INFO " Targets @ %p, sz=%d\n", hd->Targets, sz)); + + + /* Clear the TM flags + */ + hd->tmPending = 0; +#ifdef MPT_SCSI_USE_NEW_EH + hd->tmState = TM_STATE_NONE; +#endif + hd->resetPending = 0; + hd->abortSCpnt = NULL; + hd->tmPtr = NULL; + hd->numTMrequests = 0; + + /* Clear the pointer used to store + * single-threaded commands, i.e., those + * issued during a bus scan, dv and + * configuration pages. + */ + hd->cmdPtr = NULL; + + /* Attach the SCSI Host to the IOC structure + */ + this->sh = sh; + + /* Initialize this SCSI Hosts' timers + * To use, set the timer expires field + * and add_timer + */ + init_timer(&hd->timer); + hd->timer.data = (unsigned long) hd; + hd->timer.function = mptscsih_timer_expired; + + init_timer(&hd->TMtimer); + hd->TMtimer.data = (unsigned long) hd; + hd->TMtimer.function = mptscsih_taskmgmt_timeout; + hd->qtag_tick = jiffies; + + /* Moved Earlier Pam D */ + /* this->sh = sh; */ + +#ifdef MPTSCSIH_DBG_TIMEOUT + hd->ioc->timeout_hard = 0; + hd->ioc->timeout_delta = 30 * HZ; + hd->ioc->timeout_maxcnt = 0; + hd->ioc->timeout_cnt = 0; + for (ii=0; ii < 8; ii++) + foo_to[ii] = NULL; +#endif + + if (hd->is_spi) { + /* Update with the driver setup + * values. + */ + if (hd->ioc->spi_data.maxBusWidth > driver_setup.max_width) + hd->ioc->spi_data.maxBusWidth = driver_setup.max_width; + if (hd->ioc->spi_data.minSyncFactor < driver_setup.min_sync_fac) + hd->ioc->spi_data.minSyncFactor = driver_setup.min_sync_fac; + + if (hd->ioc->spi_data.minSyncFactor == MPT_ASYNC) + hd->ioc->spi_data.maxSyncOffset = 0; + + hd->negoNvram = 0; +#ifdef MPTSCSIH_DISABLE_DOMAIN_VALIDATION + hd->negoNvram = MPT_SCSICFG_USE_NVRAM; +#endif + if (driver_setup.dv == 0) + hd->negoNvram = MPT_SCSICFG_USE_NVRAM; + + hd->ioc->spi_data.forceDv = 0; + for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) + hd->ioc->spi_data.dvStatus[ii] = MPT_SCSICFG_NEGOTIATE; + + if (hd->negoNvram == 0) { + for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) + hd->ioc->spi_data.dvStatus[ii] |= MPT_SCSICFG_DV_NOT_DONE; + } + + ddvprintk((MYIOC_s_INFO_FMT + "dv %x width %x factor %x \n", + hd->ioc->name, driver_setup.dv, + driver_setup.max_width, + driver_setup.min_sync_fac)); + } + + mpt_scsi_hosts++; + + } + else { + /* SAE: Nice to know if this is failing */ + printk(KERN_WARNING MYNAM ": failed scsi_register\n"); + } + + this = mpt_adapter_find_next(this); + } + +done: + if (mpt_scsi_hosts > 0) { +/* SAE: */ +#if XENO_KILLED + register_reboot_notifier(&mptscsih_notifier); +#endif + } + else { + mpt_reset_deregister(ScsiDoneCtx); + dprintk((KERN_INFO MYNAM ": Deregistered for IOC reset notifications\n")); + + mpt_event_deregister(ScsiDoneCtx); + dprintk((KERN_INFO MYNAM ": Deregistered for IOC event notifications\n")); + + mpt_deregister(ScsiScanDvCtx); + mpt_deregister(ScsiTaskCtx); + mpt_deregister(ScsiDoneCtx); + + if (info_kbuf != NULL) + kfree(info_kbuf); + } + + return mpt_scsi_hosts; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_release - Unregister SCSI host from linux scsi mid-layer + * @host: Pointer to Scsi_Host structure + * + * (linux Scsi_Host_Template.release routine) + * This routine releases all resources associated with the SCSI host + * adapter. + * + * Returns 0 for success. + */ +int +mptscsih_release(struct Scsi_Host *host) +{ + MPT_SCSI_HOST *hd; + int count; + unsigned long flags; + + hd = (MPT_SCSI_HOST *) host->hostdata; + +#ifndef MPT_SCSI_USE_NEW_EH +#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION + spin_lock_irqsave(&dvtaskQ_lock, flags); + dvtaskQ_release = 1; + spin_unlock_irqrestore(&dvtaskQ_lock, flags); +#endif + + count = 10 * HZ; + spin_lock_irqsave(&mytaskQ_lock, flags); + if (mytaskQ_bh_active) { + spin_unlock_irqrestore(&mytaskQ_lock, flags); + dprintk((KERN_INFO MYNAM ": Info: Zapping TaskMgmt thread!\n")); + clean_taskQ(hd); + + while(mytaskQ_bh_active && --count) { +/* SAE: */ +#ifdef XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); +#else + mdelay(1); +#endif + } + } else { + spin_unlock_irqrestore(&mytaskQ_lock, flags); + } + if (!count) + printk(KERN_ERR MYNAM ": ERROR - TaskMgmt thread still active!\n"); + +#endif + +#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION + /* Check DV thread active */ + count = 10 * HZ; + spin_lock_irqsave(&dvtaskQ_lock, flags); + if (dvtaskQ_active) { + spin_unlock_irqrestore(&dvtaskQ_lock, flags); + while(dvtaskQ_active && --count) { +/* SAE: */ +#ifdef XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(1); +#else + mdelay(1); +#endif + } + } else { + spin_unlock_irqrestore(&dvtaskQ_lock, flags); + } + if (!count) + printk(KERN_ERR MYNAM ": ERROR - DV thread still active!\n"); +#if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY) + else + printk(KERN_ERR MYNAM ": DV thread orig %d, count %d\n", 10 * HZ, count); +#endif +#endif + +/* SAE: */ +#if XENO_KILLED + unregister_reboot_notifier(&mptscsih_notifier); +#endif + + if (hd != NULL) { + int sz1, sz2, sz3, sztarget=0; + int szr2chain = 0; + int szc2chain = 0; + int szchain = 0; + int szQ = 0; + + /* Synchronize disk caches + */ + (void) mptscsih_synchronize_cache(hd, 0); + + sz1 = sz2 = sz3 = 0; + + if (hd->ScsiLookup != NULL) { + sz1 = hd->ioc->req_depth * sizeof(void *); + kfree(hd->ScsiLookup); + hd->ScsiLookup = NULL; + } + + if (hd->ReqToChain != NULL) { + szr2chain = hd->ioc->req_depth * sizeof(int); + kfree(hd->ReqToChain); + hd->ReqToChain = NULL; + } + + if (hd->ChainToChain != NULL) { + szc2chain = hd->num_chain * sizeof(int); + kfree(hd->ChainToChain); + hd->ChainToChain = NULL; + } + + if (hd->ChainBuffer != NULL) { + sz2 = hd->num_chain * hd->ioc->req_sz; + szchain = szr2chain + szc2chain + sz2; + + pci_free_consistent(hd->ioc->pcidev, sz2, + hd->ChainBuffer, hd->ChainBufferDMA); + hd->ChainBuffer = NULL; + } + + if (hd->memQ != NULL) { + szQ = host->can_queue * sizeof(MPT_DONE_Q); + kfree(hd->memQ); + hd->memQ = NULL; + } + + if (hd->Targets != NULL) { + int max, ii; + + /* + * Free any target structures that were allocated. + */ + if (hd->is_spi) { + max = MPT_MAX_SCSI_DEVICES; + } else { + max = MPT_MAX_FC_DEVICES<256 ? MPT_MAX_FC_DEVICES : 255; + } + for (ii=0; ii < max; ii++) { + if (hd->Targets[ii]) { + kfree(hd->Targets[ii]); + hd->Targets[ii] = NULL; + sztarget += sizeof(VirtDevice); + } + } + + /* + * Free pointer array. + */ + sz3 = max * sizeof(void *); + kfree(hd->Targets); + hd->Targets = NULL; + } + + dprintk((MYIOC_s_INFO_FMT "Free'd ScsiLookup (%d), chain (%d) and Target (%d+%d) memory\n", + hd->ioc->name, sz1, szchain, sz3, sztarget)); + dprintk(("Free'd done and free Q (%d) memory\n", szQ)); + } + /* NULL the Scsi_Host pointer + */ + hd->ioc->sh = NULL; + scsi_unregister(host); + + if (mpt_scsi_hosts) { + if (--mpt_scsi_hosts == 0) { + mpt_reset_deregister(ScsiDoneCtx); + dprintk((KERN_INFO MYNAM ": Deregistered for IOC reset notifications\n")); + + mpt_event_deregister(ScsiDoneCtx); + dprintk((KERN_INFO MYNAM ": Deregistered for IOC event notifications\n")); + + mpt_deregister(ScsiScanDvCtx); + mpt_deregister(ScsiTaskCtx); + mpt_deregister(ScsiDoneCtx); + + if (info_kbuf != NULL) + kfree(info_kbuf); + } + } + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_halt - Process the reboot notification + * @nb: Pointer to a struct notifier_block (ignored) + * @event: event (SYS_HALT, SYS_RESTART, SYS_POWER_OFF) + * @buf: Pointer to a data buffer (ignored) + * + * This routine called if a system shutdown or reboot is to occur. + * + * Return NOTIFY_DONE if this is something other than a reboot message. + * NOTIFY_OK if this is a reboot message. + */ +static int +mptscsih_halt(struct notifier_block *nb, ulong event, void *buf) +{ + MPT_ADAPTER *ioc = NULL; + MPT_SCSI_HOST *hd = NULL; + + /* Ignore all messages other than reboot message + */ + if ((event != SYS_RESTART) && (event != SYS_HALT) + && (event != SYS_POWER_OFF)) + return (NOTIFY_DONE); + + for (ioc = mpt_adapter_find_first(); ioc != NULL; ioc = mpt_adapter_find_next(ioc)) { + /* Flush the cache of this adapter + */ + if (ioc->sh) { + hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; + if (hd) { + mptscsih_synchronize_cache(hd, 0); + } + } + } + +/* SAE: Gone */ +#if XENO_KILLED + unregister_reboot_notifier(&mptscsih_notifier); +#endif + return NOTIFY_OK; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_info - Return information about MPT adapter + * @SChost: Pointer to Scsi_Host structure + * + * (linux Scsi_Host_Template.info routine) + * + * Returns pointer to buffer where information was written. + */ +const char * +mptscsih_info(struct Scsi_Host *SChost) +{ + MPT_SCSI_HOST *h = NULL; + int size = 0; + + if (info_kbuf == NULL) + if ((info_kbuf = kmalloc(0x1000 /* 4Kb */, GFP_KERNEL)) == NULL) + return info_kbuf; + + h = (MPT_SCSI_HOST *)SChost->hostdata; + info_kbuf[0] = '\0'; + if (h) { + mpt_print_ioc_summary(h->ioc, info_kbuf, &size, 0, 0); + info_kbuf[size-1] = '\0'; + } + + return info_kbuf; +} + +struct info_str { + char *buffer; + int length; + int offset; + int pos; +}; + +static void copy_mem_info(struct info_str *info, char *data, int len) +{ + if (info->pos + len > info->length) + len = info->length - info->pos; + + if (info->pos + len < info->offset) { + info->pos += len; + return; + } + + if (info->pos < info->offset) { + data += (info->offset - info->pos); + len -= (info->offset - info->pos); + } + + if (len > 0) { + memcpy(info->buffer + info->pos, data, len); + info->pos += len; + } +} + +static int copy_info(struct info_str *info, char *fmt, ...) +{ + va_list args; + char buf[81]; + int len; + + va_start(args, fmt); + len = vsprintf(buf, fmt, args); + va_end(args); + + copy_mem_info(info, buf, len); + return len; +} + +/* SAE: For some reason off_t is not in any of the types.. */ +#ifndef off_t +#define off_t int +#endif + +static int mptscsih_host_info(MPT_ADAPTER *ioc, char *pbuf, off_t offset, int len) +{ + struct info_str info; + + info.buffer = pbuf; + info.length = len; + info.offset = offset; + info.pos = 0; + + copy_info(&info, "%s: %s, ", ioc->name, ioc->prod_name); + copy_info(&info, "%s%08xh, ", MPT_FW_REV_MAGIC_ID_STRING, ioc->facts.FWVersion.Word); + copy_info(&info, "Ports=%d, ", ioc->facts.NumberOfPorts); + copy_info(&info, "MaxQ=%d\n", ioc->req_depth); + + return ((info.pos > info.offset) ? info.pos - info.offset : 0); +} + +#ifndef MPTSCSIH_DBG_TIMEOUT +static int mptscsih_user_command(MPT_ADAPTER *ioc, char *pbuf, int len) +{ + /* Not yet implemented */ + return len; +} +#else +#define is_digit(c) ((c) >= '0' && (c) <= '9') +#define digit_to_bin(c) ((c) - '0') +#define is_space(c) ((c) == ' ' || (c) == '\t') + +#define UC_DBG_TIMEOUT 0x01 +#define UC_DBG_HARDRESET 0x02 + +static int skip_spaces(char *ptr, int len) +{ + int cnt, c; + + for (cnt = len; cnt > 0 && (c = *ptr++) && is_space(c); cnt --); + + return (len - cnt); +} + +static int get_int_arg(char *ptr, int len, ulong *pv) +{ + int cnt, c; + ulong v; + for (v = 0, cnt = len; cnt > 0 && (c=*ptr++) && is_digit(c); cnt --) { + v = (v * 10) + digit_to_bin(c); + } + + if (pv) + *pv = v; + + return (len - cnt); +} + + +static int is_keyword(char *ptr, int len, char *verb) +{ + int verb_len = strlen(verb); + + if (len >= strlen(verb) && !memcmp(verb, ptr, verb_len)) + return verb_len; + else + return 0; +} + +#define SKIP_SPACES(min_spaces) \ + if ((arg_len = skip_spaces(ptr,len)) < (min_spaces)) \ + return -EINVAL; \ + ptr += arg_len; \ + len -= arg_len; + +#define GET_INT_ARG(v) \ + if (!(arg_len = get_int_arg(ptr,len, &(v)))) \ + return -EINVAL; \ + ptr += arg_len; \ + len -= arg_len; + +static int mptscsih_user_command(MPT_ADAPTER *ioc, char *buffer, int length) +{ + char *ptr = buffer; + char btmp[24]; /* REMOVE */ + int arg_len; + int len = length; + int cmd; + ulong number = 1; + ulong delta = 10; + + if ((len > 0) && (ptr[len -1] == '\n')) + --len; + + if (len < 22) { + strncpy(btmp, buffer, len); + btmp[len+1]='\0'; + } else { + strncpy(btmp, buffer, 22); + btmp[23]='\0'; + } + printk("user_command: ioc %d, buffer %s, length %d\n", + ioc->id, btmp, length); + + if ((arg_len = is_keyword(ptr, len, "timeout")) != 0) + cmd = UC_DBG_TIMEOUT; + else if ((arg_len = is_keyword(ptr, len, "hardreset")) != 0) + cmd = UC_DBG_HARDRESET; + else + return -EINVAL; + + ptr += arg_len; + len -= arg_len; + + switch(cmd) { + case UC_DBG_TIMEOUT: + SKIP_SPACES(1); + GET_INT_ARG(number); + SKIP_SPACES(1); + GET_INT_ARG(delta); + break; + } + + printk("user_command: cnt=%ld delta=%ld\n", number, delta); + + if (len) + return -EINVAL; + else { + if (cmd == UC_DBG_HARDRESET) { + ioc->timeout_hard = 1; + } else if (cmd == UC_DBG_TIMEOUT) { + /* process this command ... + */ + ioc->timeout_maxcnt = 0; + ioc->timeout_delta = delta < 2 ? 2 : delta; + ioc->timeout_cnt = 0; + ioc->timeout_maxcnt = number < 8 ? number: 8; + } + } + /* Not yet implemented */ + return length; +} +#endif + +/* SAE: No proc */ +#if defined(CONFIG_PROC_FS) + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_proc_info - Return information about MPT adapter + * + * (linux Scsi_Host_Template.info routine) + * + * buffer: if write, user data; if read, buffer for user + * length: if write, return length; + * offset: if write, 0; if read, the current offset into the buffer from + * the previous read. + * hostno: scsi host number + * func: if write = 1; if read = 0 + */ +int mptscsih_proc_info(char *buffer, char **start, off_t offset, + int length, int hostno, int func) +{ + MPT_ADAPTER *ioc = NULL; + MPT_SCSI_HOST *hd = NULL; + int size = 0; + + dprintk(("Called mptscsih_proc_info: hostno=%d, func=%d\n", hostno, func)); + dprintk(("buffer %p, start=%p (%p) offset=%ld length = %d\n", + buffer, start, *start, offset, length)); + + for (ioc = mpt_adapter_find_first(); ioc != NULL; ioc = mpt_adapter_find_next(ioc)) { + if ((ioc->sh) && (ioc->sh->host_no == hostno)) { + hd = (MPT_SCSI_HOST *)ioc->sh->hostdata; + break; + } + } + if ((ioc == NULL) || (ioc->sh == NULL) || (hd == NULL)) + return 0; + + if (func) { + size = mptscsih_user_command(ioc, buffer, length); + } else { + if (start) + *start = buffer; + + size = mptscsih_host_info(ioc, buffer, offset, length); + } + + return size; +} + +#endif /* SAE: CONFIG_PROC_FS */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + static int max_qd = 1; +#if 0 +static int index_log[128]; +static int index_ent = 0; +static __inline__ void ADD_INDEX_LOG(int req_ent) +{ + int i = index_ent++; + + index_log[i & (128 - 1)] = req_ent; +} +#else +#define ADD_INDEX_LOG(req_ent) do { } while(0) +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_put_msgframe - Wrapper routine to post message frame to F/W. + * @context: Call back context (ScsiDoneCtx, ScsiScanDvCtx) + * @id: IOC id number + * @mf: Pointer to message frame + * + * Handles the call to mptbase for posting request and queue depth + * tracking. + * + * Returns none. + */ +static inline void +mptscsih_put_msgframe(int context, int id, MPT_FRAME_HDR *mf) +{ + /* Main banana... */ + atomic_inc(&queue_depth); + if (atomic_read(&queue_depth) > max_qd) { + max_qd = atomic_read(&queue_depth); + dprintk((KERN_INFO MYNAM ": Queue depth now %d.\n", max_qd)); + } + + mpt_put_msg_frame(context, id, mf); + + return; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine. + * @SCpnt: Pointer to Scsi_Cmnd structure + * @done: Pointer SCSI mid-layer IO completion function + * + * (linux Scsi_Host_Template.queuecommand routine) + * This is the primary SCSI IO start routine. Create a MPI SCSIIORequest + * from a linux Scsi_Cmnd request and send it to the IOC. + * + * Returns 0. (rtn value discarded by linux scsi mid-layer) + */ +int +mptscsih_qcmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) +{ + MPT_SCSI_HOST *hd; + MPT_FRAME_HDR *mf; + SCSIIORequest_t *pScsiReq; + VirtDevice *pTarget; + MPT_DONE_Q *buffer = NULL; + unsigned long flags; + int target; + int lun; + int datadir; + u32 datalen; + u32 scsictl; + u32 scsidir; + u32 cmd_len; + int my_idx; + int ii; + int rc; + int did_errcode; + int issueCmd; + + did_errcode = 0; + hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata; + target = SCpnt->target; + lun = SCpnt->lun; + SCpnt->scsi_done = done; + + pTarget = hd->Targets[target]; + + dmfprintk((MYIOC_s_INFO_FMT "qcmd: SCpnt=%p, done()=%p\n", + (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done)); + + if (hd->resetPending) { + /* Prevent new commands from being issued + * while reloading the FW. Reset timer to 60 seconds, + * as the FW can take some time to come ready. + * For New EH, cmds on doneQ posted to FW. + */ + did_errcode = 1; + mod_timer(&SCpnt->eh_timeout, jiffies + (HZ * 60)); + dtmprintk((MYIOC_s_WARN_FMT "qcmd: SCpnt=%p timeout + 60HZ\n", + (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt)); + goto did_error; + } + + /* + * Put together a MPT SCSI request... + */ + if ((mf = mpt_get_msg_frame(ScsiDoneCtx, hd->ioc->id)) == NULL) { + dprintk((MYIOC_s_WARN_FMT "QueueCmd, no msg frames!!\n", + hd->ioc->name)); + did_errcode = 2; + goto did_error; + } + + pScsiReq = (SCSIIORequest_t *) mf; + + my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); + + ADD_INDEX_LOG(my_idx); + + /* + * The scsi layer should be handling this stuff + * (In 2.3.x it does -DaveM) + */ + + /* BUG FIX! 19991030 -sralston + * TUR's being issued with scsictl=0x02000000 (DATA_IN)! + * Seems we may receive a buffer (datalen>0) even when there + * will be no data transfer! GRRRRR... + */ + datadir = mptscsih_io_direction(SCpnt); + if (datadir == SCSI_DATA_READ) { + datalen = SCpnt->request_bufflen; + scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */ + } else if (datadir == SCSI_DATA_WRITE) { + datalen = SCpnt->request_bufflen; + scsidir = MPI_SCSIIO_CONTROL_WRITE; /* DATA OUT (host-->ioc-->dev) */ + } else { + datalen = 0; + scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER; + } + + /* Default to untagged. Once a target structure has been allocated, + * use the Inquiry data to determine if device supports tagged. + */ + if ( pTarget + && (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES) + && (SCpnt->device->tagged_supported)) { + scsictl = scsidir | MPI_SCSIIO_CONTROL_SIMPLEQ; + } else { + scsictl = scsidir | MPI_SCSIIO_CONTROL_UNTAGGED; + } + + /* Use the above information to set up the message frame + */ + pScsiReq->TargetID = (u8) target; + pScsiReq->Bus = (u8) SCpnt->channel; + pScsiReq->ChainOffset = 0; + pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST; + pScsiReq->CDBLength = SCpnt->cmd_len; + pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE; + pScsiReq->Reserved = 0; + pScsiReq->MsgFlags = mpt_msg_flags(); + pScsiReq->LUN[0] = 0; + pScsiReq->LUN[1] = lun; + pScsiReq->LUN[2] = 0; + pScsiReq->LUN[3] = 0; + pScsiReq->LUN[4] = 0; + pScsiReq->LUN[5] = 0; + pScsiReq->LUN[6] = 0; + pScsiReq->LUN[7] = 0; + pScsiReq->Control = cpu_to_le32(scsictl); + + /* + * Write SCSI CDB into the message + * Should write from cmd_len up to 16, but skip for performance reasons. + */ + cmd_len = SCpnt->cmd_len; + for (ii=0; ii < cmd_len; ii++) + pScsiReq->CDB[ii] = SCpnt->cmnd[ii]; + + for (ii=cmd_len; ii < 16; ii++) + pScsiReq->CDB[ii] = 0; + + /* DataLength */ + pScsiReq->DataLength = cpu_to_le32(datalen); + + /* SenseBuffer low address */ + pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma + + (my_idx * MPT_SENSE_BUFFER_ALLOC)); + + /* Now add the SG list + * Always have a SGE even if null length. + */ + rc = SUCCESS; + if (datalen == 0) { + /* Add a NULL SGE */ + mptscsih_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0, + (dma_addr_t) -1); + } else { + /* Add a 32 or 64 bit SGE */ + rc = mptscsih_AddSGE(hd, SCpnt, pScsiReq, my_idx); + } + + + if (rc == SUCCESS) { + hd->ScsiLookup[my_idx] = SCpnt; + SCpnt->host_scribble = NULL; + + /* SCSI specific processing */ + issueCmd = 1; + if (hd->is_spi) { + int dvStatus = hd->ioc->spi_data.dvStatus[target]; + + if (dvStatus || hd->ioc->spi_data.forceDv) { + + /* Write SDP1 on this I/O to this target */ + if (dvStatus & MPT_SCSICFG_NEGOTIATE) { + mptscsih_writeSDP1(hd, 0, target, hd->negoNvram); + dvStatus &= ~MPT_SCSICFG_NEGOTIATE; + hd->ioc->spi_data.dvStatus[target] = dvStatus; + } else if (dvStatus & MPT_SCSICFG_BLK_NEGO) { + mptscsih_writeSDP1(hd, 0, target, MPT_SCSICFG_BLK_NEGO); + dvStatus &= ~MPT_SCSICFG_BLK_NEGO; + hd->ioc->spi_data.dvStatus[target] = dvStatus; + } + +#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION + if ((dvStatus & MPT_SCSICFG_NEED_DV) || + (hd->ioc->spi_data.forceDv & MPT_SCSICFG_NEED_DV)) { + unsigned long lflags; + /* Schedule DV if necessary */ + spin_lock_irqsave(&dvtaskQ_lock, lflags); + if (!dvtaskQ_active) { + dvtaskQ_active = 1; + spin_unlock_irqrestore(&dvtaskQ_lock, lflags); + MPT_INIT_WORK(&mptscsih_dvTask, mptscsih_domainValidation, (void *) hd); + + SCHEDULE_TASK(&mptscsih_dvTask); + } else { + spin_unlock_irqrestore(&dvtaskQ_lock, lflags); + } + hd->ioc->spi_data.forceDv &= ~MPT_SCSICFG_NEED_DV; + } + + /* Trying to do DV to this target, extend timeout. + * Wait to issue intil flag is clear + */ + if (dvStatus & MPT_SCSICFG_DV_PENDING) { + mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ); + issueCmd = 0; + } + + /* Set the DV flags. + */ + if (dvStatus & MPT_SCSICFG_DV_NOT_DONE) + mptscsih_set_dvflags(hd, pScsiReq); +#endif + } + } + +#ifdef MPTSCSIH_DBG_TIMEOUT + if (hd->ioc->timeout_cnt < hd->ioc->timeout_maxcnt) { + foo_to[hd->ioc->timeout_cnt] = SCpnt; + hd->ioc->timeout_cnt++; + //mod_timer(&SCpnt->eh_timeout, jiffies + hd->ioc->timeout_delta); + issueCmd = 0; + printk(MYIOC_s_WARN_FMT + "to pendingQ: (sc=%p, mf=%p, time=%ld)\n", + hd->ioc->name, SCpnt, mf, jiffies); + } +#endif + + if (issueCmd) { + mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf); + dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n", + hd->ioc->name, SCpnt, mf, my_idx)); + } else { + ddvtprintk((MYIOC_s_INFO_FMT "Pending cmd=%p idx %d\n", + hd->ioc->name, SCpnt, my_idx)); + /* Place this command on the pendingQ if possible */ + spin_lock_irqsave(&hd->freedoneQlock, flags); + if (!Q_IS_EMPTY(&hd->freeQ)) { + buffer = hd->freeQ.head; + Q_DEL_ITEM(buffer); + + /* Save the mf pointer + */ + buffer->argp = (void *)mf; + + /* Add to the pendingQ + */ + Q_ADD_TAIL(&hd->pendingQ.head, buffer, MPT_DONE_Q); + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + } else { + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + SCpnt->result = (DID_BUS_BUSY << 16); + SCpnt->scsi_done(SCpnt); + } + } + } else { + mptscsih_freeChainBuffers(hd, my_idx); + mpt_free_msg_frame(ScsiDoneCtx, hd->ioc->id, mf); + did_errcode = 3; + goto did_error; + } + + return 0; + +did_error: + dprintk((MYIOC_s_WARN_FMT "_qcmd did_errcode=%d (sc=%p)\n", + hd->ioc->name, did_errcode, SCpnt)); + /* Just wish OS to issue a retry */ + SCpnt->result = (DID_BUS_BUSY << 16); + spin_lock_irqsave(&hd->freedoneQlock, flags); + if (!Q_IS_EMPTY(&hd->freeQ)) { + dtmprintk((MYIOC_s_WARN_FMT "SCpnt=%p to doneQ\n", + (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt)); + buffer = hd->freeQ.head; + Q_DEL_ITEM(buffer); + + /* Set the Scsi_Cmnd pointer + */ + buffer->argp = (void *)SCpnt; + + /* Add to the doneQ + */ + Q_ADD_TAIL(&hd->doneQ.head, buffer, MPT_DONE_Q); + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + } else { + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + SCpnt->scsi_done(SCpnt); + } + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_freeChainBuffers - Function to free chain buffers associated + * with a SCSI IO request + * @hd: Pointer to the MPT_SCSI_HOST instance + * @req_idx: Index of the SCSI IO request frame. + * + * Called if SG chain buffer allocation fails and mptscsih callbacks. + * No return. + */ +static void +mptscsih_freeChainBuffers(MPT_SCSI_HOST *hd, int req_idx) +{ + MPT_FRAME_HDR *chain = NULL; + unsigned long flags; + int chain_idx; + int next; + + /* Get the first chain index and reset + * tracker state. + */ + chain_idx = hd->ReqToChain[req_idx]; + hd->ReqToChain[req_idx] = MPT_HOST_NO_CHAIN; + + while (chain_idx != MPT_HOST_NO_CHAIN) { + + /* Save the next chain buffer index */ + next = hd->ChainToChain[chain_idx]; + + /* Free this chain buffer and reset + * tracker + */ + hd->ChainToChain[chain_idx] = MPT_HOST_NO_CHAIN; + + chain = (MPT_FRAME_HDR *) (hd->ChainBuffer + + (chain_idx * hd->ioc->req_sz)); + spin_lock_irqsave(&hd->ioc->FreeQlock, flags); + Q_ADD_TAIL(&hd->FreeChainQ.head, + &chain->u.frame.linkage, MPT_FRAME_HDR); + spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); + + dmfprintk((MYIOC_s_INFO_FMT "FreeChainBuffers (index %d)\n", + hd->ioc->name, chain_idx)); + + /* handle next */ + chain_idx = next; + } + return; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Reset Handling + */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_TMHandler - Generic handler for SCSI Task Management. + * Fall through to mpt_HardResetHandler if: not operational, too many + * failed TM requests or handshake failure. + * + * @ioc: Pointer to MPT_ADAPTER structure + * @type: Task Management type + * @target: Logical Target ID for reset (if appropriate) + * @lun: Logical Unit for reset (if appropriate) + * @ctx2abort: Context for the task to be aborted (if appropriate) + * @sleepFlag: If set, use udelay instead of schedule in handshake code. + * + * Remark: Currently invoked from a non-interrupt thread (_bh). + * + * Remark: With old EH code, at most 1 SCSI TaskMgmt function per IOC + * will be active. + * + * Returns 0 for SUCCESS or -1 if FAILED. + */ +static int +mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, int sleepFlag) +{ + MPT_ADAPTER *ioc = NULL; + int rc = -1; + int doTask = 1; + u32 ioc_raw_state; + unsigned long flags; + + /* If FW is being reloaded currently, return success to + * the calling function. + */ + if (hd == NULL) + return 0; + + ioc = hd->ioc; + if (ioc == NULL) { + printk(KERN_ERR MYNAM " TMHandler" " NULL ioc!\n"); + return FAILED; + } + dtmprintk((MYIOC_s_INFO_FMT "TMHandler Entered!\n", ioc->name)); + + // SJR - CHECKME - Can we avoid this here? + // (mpt_HardResetHandler has this check...) + spin_lock_irqsave(&ioc->diagLock, flags); + if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)) { + spin_unlock_irqrestore(&ioc->diagLock, flags); + return FAILED; + } + spin_unlock_irqrestore(&ioc->diagLock, flags); + + /* Do not do a Task Management if there are + * too many failed TMs on this adapter. + */ + if (hd->numTMrequests > MPT_HOST_TOO_MANY_TM) + doTask = 0; + +#ifdef MPT_SCSI_USE_NEW_EH + /* Wait a fixed amount of time for the TM pending flag to be cleared. + * If we time out and not bus reset, then we return a FAILED status to the caller. + * The call to mptscsih_tm_pending_wait() will set the pending flag if we are + * successful. Otherwise, reload the FW. + */ + if (mptscsih_tm_pending_wait(hd) == FAILED) { + if (type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) { + nehprintk((KERN_WARNING MYNAM ": %s: TMHandler abort: " + "Timed out waiting for last TM (%d) to complete! \n", + hd->ioc->name, hd->tmPending)); + return FAILED; + } else if (type == MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET) { + nehprintk((KERN_WARNING MYNAM ": %s: TMHandler target reset: " + "Timed out waiting for last TM (%d) to complete! \n", + hd->ioc->name, hd->tmPending)); + return FAILED; + } else if (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) { + nehprintk((KERN_WARNING MYNAM ": %s: TMHandler bus reset: " + "Timed out waiting for last TM (%d) to complete! \n", + hd->ioc->name, hd->tmPending)); + if (hd->tmPending & (1 << MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS)) + return FAILED; + + doTask = 0; + } + } else { + spin_lock_irqsave(&hd->ioc->FreeQlock, flags); + hd->tmPending |= (1 << type); + spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); + } +#endif + + /* Is operational? + */ + ioc_raw_state = mpt_GetIocState(hd->ioc, 0); + +#ifdef MPT_DEBUG_RESET + if ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL) { + printk(MYIOC_s_WARN_FMT + "TM Handler: IOC Not operational(0x%x)!\n", + hd->ioc->name, ioc_raw_state); + } +#endif + + if (doTask && ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_OPERATIONAL) + && !(ioc_raw_state & MPI_DOORBELL_ACTIVE)) { + + /* Isse the Task Mgmt request. + */ + if (hd->hard_resets < -1) + hd->hard_resets++; + rc = mptscsih_IssueTaskMgmt(hd, type, channel, target, lun, ctx2abort, sleepFlag); + if (rc) { + printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt failed!\n", hd->ioc->name); + } else { + dtmprintk((MYIOC_s_INFO_FMT "Issue of TaskMgmt Successful!\n", hd->ioc->name)); + } + } +#ifdef MPTSCSIH_DBG_TIMEOUT + if (hd->ioc->timeout_hard) + rc = 1; +#endif + + /* Only fall through to the HRH if this is a bus reset + */ + if ((type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) && (rc || + ioc->reload_fw || (ioc->alt_ioc && ioc->alt_ioc->reload_fw))) { + dtmprintk((MYIOC_s_INFO_FMT "Calling HardReset! \n", + hd->ioc->name)); + rc = mpt_HardResetHandler(hd->ioc, sleepFlag); + } + + dtmprintk((MYIOC_s_INFO_FMT "TMHandler rc = %d!\n", hd->ioc->name, rc)); +#ifndef MPT_SCSI_USE_NEW_EH + dtmprintk((MYIOC_s_INFO_FMT "TMHandler: _bh_handler state (%d) taskQ count (%d)\n", + ioc->name, mytaskQ_bh_active, hd->taskQcnt)); +#endif + + return rc; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_IssueTaskMgmt - Generic send Task Management function. + * @hd: Pointer to MPT_SCSI_HOST structure + * @type: Task Management type + * @target: Logical Target ID for reset (if appropriate) + * @lun: Logical Unit for reset (if appropriate) + * @ctx2abort: Context for the task to be aborted (if appropriate) + * @sleepFlag: If set, use udelay instead of schedule in handshake code. + * + * Remark: _HardResetHandler can be invoked from an interrupt thread (timer) + * or a non-interrupt thread. In the former, must not call schedule(). + * + * Not all fields are meaningfull for all task types. + * + * Returns 0 for SUCCESS, -999 for "no msg frames", + * else other non-zero value returned. + */ +static int +mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 target, u8 lun, int ctx2abort, int sleepFlag) +{ + MPT_FRAME_HDR *mf; + SCSITaskMgmt_t *pScsiTm; + int ii; + int retval; + + /* Return Fail to calling function if no message frames available. + */ + if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) { + dtmprintk((MYIOC_s_WARN_FMT "IssueTaskMgmt, no msg frames!!\n", + hd->ioc->name)); + //return FAILED; + return -999; + } + dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt request @ %p\n", + hd->ioc->name, mf)); + + /* Format the Request + */ + pScsiTm = (SCSITaskMgmt_t *) mf; + pScsiTm->TargetID = target; + pScsiTm->Bus = channel; + pScsiTm->ChainOffset = 0; + pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT; + + pScsiTm->Reserved = 0; + pScsiTm->TaskType = type; + pScsiTm->Reserved1 = 0; + pScsiTm->MsgFlags = (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) + ? MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION : 0; + + for (ii= 0; ii < 8; ii++) { + pScsiTm->LUN[ii] = 0; + } + pScsiTm->LUN[1] = lun; + + for (ii=0; ii < 7; ii++) + pScsiTm->Reserved2[ii] = 0; + + pScsiTm->TaskMsgContext = ctx2abort; + dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt, ctx2abort (0x%08x), type (%d)\n", + hd->ioc->name, ctx2abort, type)); + + /* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake + mpt_put_msg_frame(hd->ioc->id, mf); + * Save the MF pointer in case the request times out. + */ + hd->tmPtr = mf; + hd->numTMrequests++; + hd->TMtimer.expires = jiffies + HZ*20; /* 20 seconds */ + add_timer(&hd->TMtimer); + + if ((retval = mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id, + sizeof(SCSITaskMgmt_t), (u32*)pScsiTm, sleepFlag)) + != 0) { + dtmprintk((MYIOC_s_WARN_FMT "_send_handshake FAILED!" + " (hd %p, ioc %p, mf %p) \n", hd->ioc->name, hd, hd->ioc, mf)); + hd->numTMrequests--; + hd->tmPtr = NULL; + del_timer(&hd->TMtimer); + mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf); + } + + return retval; +} + +#ifdef MPT_SCSI_USE_NEW_EH /* { */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_abort - Abort linux Scsi_Cmnd routine, new_eh variant + * @SCpnt: Pointer to Scsi_Cmnd structure, IO to be aborted + * + * (linux Scsi_Host_Template.eh_abort_handler routine) + * + * Returns SUCCESS or FAILED. + */ +int +mptscsih_abort(Scsi_Cmnd * SCpnt) +{ + MPT_SCSI_HOST *hd; + MPT_FRAME_HDR *mf; + u32 ctx2abort; + int scpnt_idx; + + /* If we can't locate our host adapter structure, return FAILED status. + */ + if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) { + SCpnt->result = DID_RESET << 16; + SCpnt->scsi_done(SCpnt); + nehprintk((KERN_WARNING MYNAM ": mptscsih_abort: " + "Can't locate host! (sc=%p)\n", + SCpnt)); + return FAILED; + } + + if (hd->resetPending) + return FAILED; + + printk(KERN_WARNING MYNAM ": %s: >> Attempting task abort! (sc=%p, numIOs=%d)\n", + hd->ioc->name, SCpnt, atomic_read(&queue_depth)); + + if (hd->timeouts < -1) + hd->timeouts++; + + /* Find this command + */ + if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) { + /* Cmd not found in ScsiLookup. If found in + * doneQ, delete from Q. Do OS callback. + */ + search_doneQ_for_cmd(hd, SCpnt); + + SCpnt->result = DID_RESET << 16; + SCpnt->scsi_done(SCpnt); + nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: " + "Command not in the active list! (sc=%p)\n", + hd->ioc->name, SCpnt)); + return SUCCESS; + } + + /* If this command is pended, then timeout/hang occurred + * during DV. Post command and flush pending Q + * and then following up with the reset request. + */ + if ((mf = mptscsih_search_pendingQ(hd, scpnt_idx)) != NULL) { + mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf); + post_pendingQ_commands(hd); + nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: " + "Posting pended cmd! (sc=%p)\n", + hd->ioc->name, SCpnt)); + } + + /* Most important! Set TaskMsgContext to SCpnt's MsgContext! + * (the IO to be ABORT'd) + * + * NOTE: Since we do not byteswap MsgContext, we do not + * swap it here either. It is an opaque cookie to + * the controller, so it does not matter. -DaveM + */ + mf = MPT_INDEX_2_MFPTR(hd->ioc, scpnt_idx); + ctx2abort = mf->u.frame.hwhdr.msgctxu.MsgContext; + + hd->abortSCpnt = SCpnt; + if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK, + SCpnt->channel, SCpnt->target, SCpnt->lun, ctx2abort, NO_SLEEP) + < 0) { + + /* The TM request failed and the subsequent FW-reload failed! + * Fatal error case. + */ + printk(MYIOC_s_WARN_FMT "Error issuing abort task! (sc=%p)\n", + hd->ioc->name, SCpnt); + + /* We must clear our pending flag before clearing our state. + */ + hd->tmPending = 0; + hd->tmState = TM_STATE_NONE; + + return FAILED; + } + return FAILED; + +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_dev_reset - Perform a SCSI TARGET_RESET! new_eh variant + * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to + * + * (linux Scsi_Host_Template.eh_dev_reset_handler routine) + * + * Returns SUCCESS or FAILED. + */ +int +mptscsih_dev_reset(Scsi_Cmnd * SCpnt) +{ + MPT_SCSI_HOST *hd; + + /* If we can't locate our host adapter structure, return FAILED status. + */ + if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL){ + nehprintk((KERN_WARNING MYNAM ": mptscsih_dev_reset: " + "Can't locate host! (sc=%p)\n", + SCpnt)); + return FAILED; + } + + if (hd->resetPending) + return FAILED; + + printk(KERN_WARNING MYNAM ": %s: >> Attempting target reset! (sc=%p, numIOs=%d)\n", + hd->ioc->name, SCpnt, atomic_read(&queue_depth)); + + /* Unsupported for SCSI. Supported for FCP + */ + if (hd->is_spi) + return FAILED; + + if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET, + SCpnt->channel, SCpnt->target, 0, 0, NO_SLEEP) + < 0){ + /* The TM request failed and the subsequent FW-reload failed! + * Fatal error case. + */ + printk(MYIOC_s_WARN_FMT "Error processing TaskMgmt request (sc=%p)\n", + hd->ioc->name, SCpnt); + hd->tmPending = 0; + hd->tmState = TM_STATE_NONE; + return FAILED; + } + return SUCCESS; + +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_bus_reset - Perform a SCSI BUS_RESET! new_eh variant + * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to + * + * (linux Scsi_Host_Template.eh_bus_reset_handler routine) + * + * Returns SUCCESS or FAILED. + */ +int +mptscsih_bus_reset(Scsi_Cmnd * SCpnt) +{ + MPT_SCSI_HOST *hd; + + /* If we can't locate our host adapter structure, return FAILED status. + */ + if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL){ + nehprintk((KERN_WARNING MYNAM ": mptscsih_bus_reset: " + "Can't locate host! (sc=%p)\n", + SCpnt ) ); + return FAILED; + } + + printk(KERN_WARNING MYNAM ": %s: >> Attempting bus reset! (sc=%p, numIOs=%d)\n", + hd->ioc->name, SCpnt, atomic_read(&queue_depth)); + + if (hd->timeouts < -1) + hd->timeouts++; + + /* We are now ready to execute the task management request. */ + if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, + SCpnt->channel, 0, 0, 0, NO_SLEEP) + < 0){ + + /* The TM request failed and the subsequent FW-reload failed! + * Fatal error case. + */ + printk(MYIOC_s_WARN_FMT + "Error processing TaskMgmt request (sc=%p)\n", + hd->ioc->name, SCpnt); + hd->tmPending = 0; + hd->tmState = TM_STATE_NONE; + return FAILED; + } + + return SUCCESS; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_host_reset - Perform a SCSI host adapter RESET! + * new_eh variant + * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to + * + * (linux Scsi_Host_Template.eh_host_reset_handler routine) + * + * Returns SUCCESS or FAILED. + */ +int +mptscsih_host_reset(Scsi_Cmnd *SCpnt) +{ + MPT_SCSI_HOST * hd; + int status = SUCCESS; + + /* If we can't locate the host to reset, then we failed. */ + if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL){ + nehprintk( ( KERN_WARNING MYNAM ": mptscsih_host_reset: " + "Can't locate host! (sc=%p)\n", + SCpnt ) ); + return FAILED; + } + + printk(KERN_WARNING MYNAM ": %s: >> Attempting host reset! (sc=%p)\n", + hd->ioc->name, SCpnt); + printk(KERN_WARNING MYNAM ": %s: IOs outstanding = %d\n", + hd->ioc->name, atomic_read(&queue_depth)); + + /* If our attempts to reset the host failed, then return a failed + * status. The host will be taken off line by the SCSI mid-layer. + */ + if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0){ + status = FAILED; + } else { + /* Make sure TM pending is cleared and TM state is set to + * NONE. + */ + hd->tmPending = 0; + hd->tmState = TM_STATE_NONE; + } + + + nehprintk( ( KERN_WARNING MYNAM ": mptscsih_host_reset: " + "Status = %s\n", + (status == SUCCESS) ? "SUCCESS" : "FAILED" ) ); + + return status; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_tm_pending_wait - wait for pending task management request to + * complete. + * @hd: Pointer to MPT host structure. + * + * Returns {SUCCESS,FAILED}. + */ +static int +mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd) +{ + unsigned long flags; + int loop_count = 2 * 10 * 4; /* Wait 2 seconds */ + int status = FAILED; + + do { + spin_lock_irqsave(&hd->ioc->FreeQlock, flags); + if (hd->tmState == TM_STATE_NONE) { + hd->tmState = TM_STATE_IN_PROGRESS; + hd->tmPending = 1; + spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); + status = SUCCESS; + break; + } + spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); + //set_current_state(TASK_INTERRUPTIBLE); + //schedule_timeout(HZ/4); + mdelay(250); + } while (--loop_count); + + return status; +} + +#else /* MPT_SCSI old EH stuff... */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_old_abort - Abort linux Scsi_Cmnd routine + * @SCpnt: Pointer to Scsi_Cmnd structure, IO to be aborted + * + * (linux Scsi_Host_Template.abort routine) + * + * Returns SCSI_ABORT_{SUCCESS,BUSY,PENDING}. + */ +int +mptscsih_old_abort(Scsi_Cmnd *SCpnt) +{ + MPT_SCSI_HOST *hd; + MPT_FRAME_HDR *mf; + struct mpt_work_struct *ptaskfoo; + unsigned long flags; + int scpnt_idx; + + printk(KERN_WARNING MYNAM ": OldAbort scheduling ABORT SCSI IO (sc=%p)\n", (void *) SCpnt); + printk(KERN_WARNING " IOs outstanding = %d\n", atomic_read(&queue_depth)); + + if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) { + printk(KERN_WARNING " WARNING - OldAbort, NULL hostdata ptr!!\n"); + SCpnt->result = DID_ERROR << 16; + SCpnt->scsi_done(SCpnt); + return SCSI_ABORT_NOT_RUNNING; + } + + if (hd->timeouts < -1) + hd->timeouts++; + + if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) { + /* Cmd not found in ScsiLookup. + * If found in doneQ, delete from Q. + * Do OS callback. + */ + search_doneQ_for_cmd(hd, SCpnt); + + SCpnt->result = DID_RESET << 16; + SCpnt->scsi_done(SCpnt); + return SCSI_ABORT_SUCCESS; + } else { + /* If this command is pended, then timeout/hang occurred + * during DV. Force bus reset by posting command to F/W + * and then following up with the reset request. + */ + if ((mf = mptscsih_search_pendingQ(hd, scpnt_idx)) != NULL) { + mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf); + post_pendingQ_commands(hd); + } + } + + /* + * Check to see if there's already an ABORT queued for this guy. + */ + mf = search_taskQ(0, SCpnt, hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK); + if (mf != NULL) { + dtmprintk((MYIOC_s_INFO_FMT "OldAbort:Abort Task PENDING cmd (%p) taskQ depth (%d)\n", + hd->ioc->name, SCpnt, hd->taskQcnt)); + return SCSI_ABORT_PENDING; + } + + // SJR - CHECKME - Can we avoid this here? + // (mpt_HardResetHandler has this check...) + /* If IOC is reloading FW, return PENDING. + */ + spin_lock_irqsave(&hd->ioc->diagLock, flags); + if (hd->ioc->diagPending) { + spin_unlock_irqrestore(&hd->ioc->diagLock, flags); + return SCSI_ABORT_PENDING; + } + spin_unlock_irqrestore(&hd->ioc->diagLock, flags); + + /* If there are no message frames what should we do? + */ + if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) { + printk((KERN_WARNING " WARNING - OldAbort, no msg frames!!\n")); + /* We are out of message frames! + * Call the reset handler to do a FW reload. + */ + printk((KERN_WARNING " Reloading Firmware!!\n")); + if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) { + printk((KERN_WARNING " Firmware Reload FAILED!!\n")); + } + return SCSI_ABORT_PENDING; + } + + /* + * Add ourselves to (end of) taskQ . + * Check to see if our _bh is running. If NOT, schedule it. + */ + spin_lock_irqsave(&hd->ioc->FreeQlock, flags); + Q_ADD_TAIL(&hd->taskQ, &mf->u.frame.linkage, MPT_FRAME_HDR); + hd->taskQcnt++; + atomic_inc(&mpt_taskQdepth); + spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); + + spin_lock_irqsave(&mytaskQ_lock, flags); + + /* Save the original SCpnt mf pointer + */ + SCpnt->host_scribble = (u8 *) MPT_INDEX_2_MFPTR (hd->ioc, scpnt_idx); + + /* For the time being, force bus reset on any abort + * requests for the 1030/1035 FW. + */ + if (hd->is_spi) + mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS; + else + mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK; + + mf->u.frame.linkage.argp1 = SCpnt; + mf->u.frame.linkage.argp2 = (void *) hd; + + dtmprintk((MYIOC_s_INFO_FMT "OldAbort:_bh_handler state (%d) taskQ count (%d)\n", + hd->ioc->name, mytaskQ_bh_active, hd->taskQcnt)); + + if (! mytaskQ_bh_active) { + mytaskQ_bh_active = 1; + spin_unlock_irqrestore(&mytaskQ_lock, flags); + + ptaskfoo = (struct mpt_work_struct *) &mptscsih_ptaskfoo; + MPT_INIT_WORK(&mptscsih_ptaskfoo, mptscsih_taskmgmt_bh, (void *) SCpnt); + + SCHEDULE_TASK(ptaskfoo); + } else { + spin_unlock_irqrestore(&mytaskQ_lock, flags); + } + + return SCSI_ABORT_PENDING; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_old_reset - Perform a SCSI BUS_RESET! + * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to + * @reset_flags: (not used?) + * + * (linux Scsi_Host_Template.reset routine) + * + * Returns SCSI_RESET_{SUCCESS,PUNT,PENDING}. + */ +int +mptscsih_old_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags) +{ + MPT_SCSI_HOST *hd; + MPT_FRAME_HDR *mf; + struct mpt_work_struct *ptaskfoo; + unsigned long flags; + int scpnt_idx; + + printk(KERN_WARNING MYNAM ": OldReset scheduling BUS_RESET (sc=%p)\n", (void *) SCpnt); + printk(KERN_WARNING " IOs outstanding = %d\n", atomic_read(&queue_depth)); + + if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) { + SCpnt->result = DID_RESET << 16; + SCpnt->scsi_done(SCpnt); + return SCSI_RESET_SUCCESS; + } + + if (hd->timeouts < -1) + hd->timeouts++; + + if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) { + /* Cmd not found in ScsiLookup. + * If found in doneQ, delete from Q. + * Do OS callback. + */ + search_doneQ_for_cmd(hd, SCpnt); + + SCpnt->result = DID_RESET << 16; + SCpnt->scsi_done(SCpnt); + return SCSI_RESET_SUCCESS; + } else { + /* If this command is pended, then timeout/hang occurred + * during DV. Force bus reset by posting command to F/W + * and then following up with the reset request. + */ + if ((mf = mptscsih_search_pendingQ(hd, scpnt_idx)) != NULL) { + mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf); + post_pendingQ_commands(hd); + } + } + + /* + * Check to see if there's an ABORT_TASK queued for this guy. + * If so, delete. + */ + search_taskQ(1, SCpnt, hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK); + + /* + * Check to see if there's already a BUS_RESET queued for this guy. + */ + mf = search_taskQ(0, SCpnt, hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS); + if (mf != NULL) { + dtmprintk((MYIOC_s_INFO_FMT "OldReset:Reset Task PENDING cmd (%p) taskQ depth (%d)\n", + hd->ioc->name, SCpnt, hd->taskQcnt)); + return SCSI_RESET_PENDING; + } + + // SJR - CHECKME - Can we avoid this here? + // (mpt_HardResetHandler has this check...) + /* If IOC is reloading FW, return PENDING. + */ + spin_lock_irqsave(&hd->ioc->diagLock, flags); + if (hd->ioc->diagPending) { + spin_unlock_irqrestore(&hd->ioc->diagLock, flags); + return SCSI_RESET_PENDING; + } + spin_unlock_irqrestore(&hd->ioc->diagLock, flags); + + if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) { + /* We are out of message frames! + * Call the reset handler to do a FW reload. + */ + printk((KERN_WARNING " Reloading Firmware!!\n")); + if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) { + printk((KERN_WARNING " Firmware Reload FAILED!!\n")); + } + return SCSI_RESET_PENDING; + } + + /* + * Add ourselves to (end of) taskQ. + * Check to see if our _bh is running. If NOT, schedule it. + */ + spin_lock_irqsave(&hd->ioc->FreeQlock, flags); + Q_ADD_TAIL(&hd->taskQ, &mf->u.frame.linkage, MPT_FRAME_HDR); + hd->taskQcnt++; + atomic_inc(&mpt_taskQdepth); + spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); + + + dtmprintk((MYIOC_s_INFO_FMT "OldReset: _bh_handler state (%d) taskQ count (%d)\n", + hd->ioc->name, mytaskQ_bh_active, hd->taskQcnt)); + + spin_lock_irqsave(&mytaskQ_lock, flags); + /* Save the original SCpnt mf pointer + */ + SCpnt->host_scribble = (u8 *) MPT_INDEX_2_MFPTR (hd->ioc, scpnt_idx); + + mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS; + mf->u.frame.linkage.argp1 = SCpnt; + mf->u.frame.linkage.argp2 = (void *) hd; + + if (! mytaskQ_bh_active) { + mytaskQ_bh_active = 1; + spin_unlock_irqrestore(&mytaskQ_lock, flags); + /* + * Oh how cute, no alloc/free/mgmt needed if we use + * (bottom/unused portion of) MPT request frame. + */ + ptaskfoo = (struct mpt_work_struct *) &mptscsih_ptaskfoo; + MPT_INIT_WORK(&mptscsih_ptaskfoo, mptscsih_taskmgmt_bh, (void *) SCpnt); + + SCHEDULE_TASK(ptaskfoo); + } else { + spin_unlock_irqrestore(&mytaskQ_lock, flags); + } + return SCSI_RESET_PENDING; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_taskmgmt_bh - SCSI task mgmt bottom half handler + * @sc: (unused) + * + * This routine (thread) is active whenever there are any outstanding + * SCSI task management requests for a SCSI host adapter. + * IMPORTANT! This routine is scheduled therefore should never be + * running in ISR context. i.e., it's safe to sleep here. + */ +void +mptscsih_taskmgmt_bh(void *sc) +{ + MPT_ADAPTER *ioc; + Scsi_Cmnd *SCpnt; + MPT_FRAME_HDR *mf = NULL; + MPT_SCSI_HOST *hd; + u32 ctx2abort = 0; + unsigned long flags; + int scpnt_idx; + int did; + u8 task_type; + + spin_lock_irqsave(&mytaskQ_lock, flags); + mytaskQ_bh_active = 1; + spin_unlock_irqrestore(&mytaskQ_lock, flags); + + do { +/* SAE: Not in kernel thread */ +#ifdef XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/4); +#else + /* SAE: Trying a lower value... 250 -> 100 */ + mdelay(100); +#endif + did = 0; + + for (ioc = mpt_adapter_find_first(); ioc != NULL; ioc = mpt_adapter_find_next(ioc)) { + if (ioc->sh) { + hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; + if (hd == NULL) { + printk(KERN_ERR MYNAM + ": ERROR - TaskMgmt NULL SCSI Host!" + "(ioc=%p, sh=%p hd=%p)\n", + (void *) ioc, (void *) ioc->sh, (void *) hd); + continue; + } + +#ifdef MPTSCSIH_DBG_TIMEOUT + if (ioc->timeout_hard == 1) { + mptscsih_TMHandler(hd, + MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, + 0, 0, 0, 0, CAN_SLEEP); + + } +#endif + + spin_lock_irqsave(&ioc->FreeQlock, flags); + if (Q_IS_EMPTY(&hd->taskQ)) { + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + continue; + } + + /* If we ever find a non-empty queue, + * keep the handler alive + */ + did++; + + /* tmPending is SMP lock-protected */ + if (hd->tmPending || hd->tmPtr) { + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + continue; + } + hd->tmPending = 1; + + /* Process this request + */ + mf = hd->taskQ.head; + Q_DEL_ITEM(&mf->u.frame.linkage); + hd->taskQcnt--; + atomic_dec(&mpt_taskQdepth); + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + + SCpnt = (Scsi_Cmnd*)mf->u.frame.linkage.argp1; + if (SCpnt == NULL) { + printk(KERN_ERR MYNAM ": ERROR - TaskMgmt has NULL SCpnt! (mf=%p:sc=%p)\n", + (void *) mf, (void *) SCpnt); + mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf); + spin_lock_irqsave(&ioc->FreeQlock, flags); + hd->tmPending = 0; + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + continue; + } + + /* Get the ScsiLookup index pointer + * from the SC pointer. + */ + if (!SCpnt->host_scribble || ((MPT_SCSI_HOST *)SCpnt->host->hostdata != hd)) { + /* The command associated with the + * abort/reset request must have + * completed and this is a stale + * request. We are done. + * Free the current MF and continue. + */ + mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf); + spin_lock_irqsave(&ioc->FreeQlock, flags); + hd->tmPending = 0; + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + continue; + } + + scpnt_idx = MFPTR_2_MPT_INDEX(hd->ioc, SCpnt->host_scribble); + if (scpnt_idx != SCPNT_TO_LOOKUP_IDX(SCpnt)) { + /* Error! this should never happen!! + */ + mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf); + spin_lock_irqsave(&ioc->FreeQlock, flags); + hd->tmPending = 0; + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + continue; + } + + task_type = mf->u.frame.linkage.arg1; + ctx2abort = 0; + if (task_type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) { + MPT_FRAME_HDR *SCpntMf; + + /* + * Most important! Set TaskMsgContext to SCpnt's MsgContext! + * (the IO to be ABORT'd) + * + * NOTE: Since we do not byteswap MsgContext, we do not + * swap it here either. It is an opaque cookie to + * the controller, so it does not matter. -DaveM + */ + SCpntMf = (MPT_FRAME_HDR *) SCpnt->host_scribble; + ctx2abort = SCpntMf->u.frame.hwhdr.msgctxu.MsgContext; + + hd->abortSCpnt = SCpnt; + printk(KERN_WARNING MYNAM ": Attempting ABORT SCSI IO! (mf=%p:sc=%p)\n", + (void *) mf, (void *) SCpnt); + } + + /* The TM handler will allocate a new mf, + * so free the current mf. + */ + mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf); + mf = NULL; + + if (mptscsih_TMHandler(hd, task_type, SCpnt->channel, + SCpnt->target, SCpnt->lun, + ctx2abort, CAN_SLEEP) < 0) { + + /* The TM request failed and the subsequent FW-reload failed! + * Fatal error case. + */ + printk(KERN_WARNING MYNAM + ": WARNING[1] - IOC error processing TaskMgmt request (sc=%p)\n", (void *) SCpnt); + + if (hd->ScsiLookup[scpnt_idx] != NULL) { + atomic_dec(&queue_depth); + SCpnt->result = DID_SOFT_ERROR << 16; + MPT_HOST_LOCK(flags); + SCpnt->scsi_done(SCpnt); + MPT_HOST_UNLOCK(flags); + mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf); + } + spin_lock_irqsave(&ioc->FreeQlock, flags); + hd->tmPending = 0; + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + hd->abortSCpnt = NULL; + } + } + } + if (atomic_read(&mpt_taskQdepth) > 0) + did++; + + } while ( did ); + + spin_lock_irqsave(&mytaskQ_lock, flags); + mytaskQ_bh_active = 0; + spin_unlock_irqrestore(&mytaskQ_lock, flags); + + return; +} +#endif /* } */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_taskmgmt_complete - Registered with Fusion MPT base driver + * @ioc: Pointer to MPT_ADAPTER structure + * @mf: Pointer to SCSI task mgmt request frame + * @mr: Pointer to SCSI task mgmt reply frame + * + * This routine is called from mptbase.c::mpt_interrupt() at the completion + * of any SCSI task management request. + * This routine is registered with the MPT (base) driver at driver + * load/init time via the mpt_register() API call. + * + * Returns 1 indicating alloc'd request frame ptr should be freed. + */ +static int +mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) +{ + SCSITaskMgmtReply_t *pScsiTmReply; + SCSITaskMgmt_t *pScsiTmReq; + MPT_SCSI_HOST *hd = NULL; + unsigned long flags; + u8 tmType = 0; + + dtmprintk((MYIOC_s_INFO_FMT "SCSI TaskMgmt completed (mf=%p,r=%p)\n", + ioc->name, mf, mr)); + + if (ioc->sh) { + /* Depending on the thread, a timer is activated for + * the TM request. Delete this timer on completion of TM. + * Decrement count of outstanding TM requests. + */ + hd = (MPT_SCSI_HOST *)ioc->sh->hostdata; + if (hd->tmPtr) { + del_timer(&hd->TMtimer); + } + dtmprintk((MYIOC_s_INFO_FMT "taskQcnt (%d)\n", + ioc->name, hd->taskQcnt)); + } else { + dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt Complete: NULL Scsi Host Ptr\n", + ioc->name)); + return 1; + } + + if (mr == NULL) { + dtmprintk((MYIOC_s_WARN_FMT "ERROR! TaskMgmt Reply: NULL Request %p\n", + ioc->name, mf)); + return 1; + } else { + pScsiTmReply = (SCSITaskMgmtReply_t*)mr; + pScsiTmReq = (SCSITaskMgmt_t*)mf; + + /* Figure out if this was ABORT_TASK, TARGET_RESET, or BUS_RESET! */ + tmType = pScsiTmReq->TaskType; + + dtmprintk((KERN_INFO " TaskType = %d, TerminationCount=%d\n", + tmType, le32_to_cpu(pScsiTmReply->TerminationCount))); + + /* Error? (anything non-zero?) */ + if (*(u32 *)&pScsiTmReply->Reserved2[0]) { + u16 iocstatus; + + iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK; + dtmprintk((KERN_INFO " SCSI TaskMgmt (%d) - Oops!\n", tmType)); + dtmprintk((KERN_INFO " IOCStatus = %04xh\n", iocstatus)); + dtmprintk((KERN_INFO " IOCLogInfo = %08xh\n", + le32_to_cpu(pScsiTmReply->IOCLogInfo))); + + /* clear flags and continue. + */ + if (tmType == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) + hd->abortSCpnt = NULL; + + /* If an internal command is present + * or the TM failed - reload the FW. + * FC FW may respond FAILED to an ABORT + */ + if (tmType == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) { + if ((hd->cmdPtr) || + (iocstatus == MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED)) { + if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0) { + printk((KERN_WARNING + " Firmware Reload FAILED!!\n")); + } + } + } + } else { + dtmprintk((KERN_INFO " SCSI TaskMgmt SUCCESS!\n")); + +#ifndef MPT_SCSI_USE_NEW_EH + if (tmType == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) { + /* clean taskQ - remove tasks associated with + * completed commands. + */ + clean_taskQ(hd); + } else if (tmType == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) { + /* If taskQ contains another request + * for this SCpnt, delete this request. + */ + search_taskQ_for_cmd(hd->abortSCpnt, hd); + } +#endif + hd->numTMrequests--; + hd->abortSCpnt = NULL; + flush_doneQ(hd); + + } + } + +#ifndef MPT_SCSI_USE_NEW_EH + /* + * Signal to _bh thread that we finished. + * This IOC can now process another TM command. + */ + dtmprintk((MYIOC_s_INFO_FMT "taskmgmt_complete: (=%p) done! Num Failed(%d) Task Count (%d)\n", + ioc->name, mf, hd->numTMrequests, hd->taskQcnt)); +#endif + hd->tmPtr = NULL; + spin_lock_irqsave(&ioc->FreeQlock, flags); + hd->tmPending = 0; + spin_unlock_irqrestore(&ioc->FreeQlock, flags); +#ifdef MPT_SCSI_USE_NEW_EH + hd->tmState = TM_STATE_NONE; +#endif + + return 1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * This is anyones guess quite frankly. + */ +int +mptscsih_bios_param(Disk * disk, kdev_t dev, int *ip) +{ + unsigned capacity = disk->capacity; + int size; + + size = capacity; + ip[0] = 64; /* heads */ + ip[1] = 32; /* sectors */ + if ((ip[2] = size >> 11) > 1024) { /* cylinders, test for big disk */ + ip[0] = 255; /* heads */ + ip[1] = 63; /* sectors */ + ip[2] = size / (255 * 63); /* cylinders */ + } + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * OS entry point to adjust the queue_depths on a per-device basis. + * Called once per device the bus scan. Use it to force the queue_depth + * member to 1 if a device does not support Q tags. + */ +void +mptscsih_select_queue_depths(struct Scsi_Host *sh, Scsi_Device *sdList) +{ + struct scsi_device *device; + VirtDevice *pTarget; + MPT_SCSI_HOST *hd; + + for (device = sdList; device != NULL; device = device->next) { + + if (device->host != sh) + continue; + + hd = (MPT_SCSI_HOST *) sh->hostdata; + if (hd == NULL) + continue; + + if (hd->Targets != NULL) { + pTarget = NULL; + if (device->id > sh->max_id) { + /* error case, should never happen */ + device->queue_depth = 1; + continue; + } else { + pTarget = hd->Targets[device->id]; + } + + if (pTarget == NULL) { + /* error case - don't know about this device */ + device->queue_depth = 1; + } else if (pTarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) { + if (!(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)) + device->queue_depth = 1; + else if (((pTarget->inq_data[0] & 0x1f) == 0x00) + && (pTarget->minSyncFactor <= MPT_ULTRA160 || !hd->is_spi)){ + device->queue_depth = MPT_SCSI_CMD_PER_DEV_HIGH; + } else + device->queue_depth = MPT_SCSI_CMD_PER_DEV_LOW; + + } else { + /* error case - No Inq. Data */ + device->queue_depth = 1; + } + + if (pTarget != NULL) { + printk(MYIOC_s_INFO_FMT + "scsi%d: Id=%d Lun=%d: Queue depth=%d\n", + hd->ioc->name, sh->host_no, + device->id, device->lun, device->queue_depth); + + dprintk((MYIOC_s_INFO_FMT + "Id = %d, sync factor = %x\n", + hd->ioc->name, pTarget->target_id, + pTarget->minSyncFactor)); + } + } + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Private routines... + */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* Utility function to copy sense data from the scsi_cmnd buffer + * to the FC and SCSI target structures. + * + */ +static void +copy_sense_data(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply) +{ + VirtDevice *target; + SCSIIORequest_t *pReq; + u32 sense_count = le32_to_cpu(pScsiReply->SenseCount); + int index; + char devFoo[96]; + IO_Info_t thisIo; + + /* Get target structure + */ + pReq = (SCSIIORequest_t *) mf; + index = (int) pReq->TargetID; + target = hd->Targets[index]; + if (hd->is_multipath && sc->device->hostdata) + target = (VirtDevice *) sc->device->hostdata; + + if (sense_count) { + u8 *sense_data; + int req_index; + + /* Copy the sense received into the scsi command block. */ + req_index = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); + sense_data = ((u8 *)hd->ioc->sense_buf_pool + (req_index * MPT_SENSE_BUFFER_ALLOC)); + memcpy(sc->sense_buffer, sense_data, SNS_LEN(sc)); + + /* Log SMART data (asc = 0x5D, non-IM case only) if required. + */ + if ((hd->ioc->events) && (hd->ioc->eventTypes & (1 << MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE))) { + if ((sense_data[12] == 0x5D) && (target->raidVolume == 0)) { + int idx; + MPT_ADAPTER *ioc = hd->ioc; + + idx = ioc->eventContext % ioc->eventLogSize; + ioc->events[idx].event = MPI_EVENT_SCSI_DEVICE_STATUS_CHANGE; + ioc->events[idx].eventContext = ioc->eventContext; + + ioc->events[idx].data[0] = (pReq->LUN[1] << 24) || + (MPI_EVENT_SCSI_DEV_STAT_RC_SMART_DATA << 16) || + (pReq->Bus << 8) || pReq->TargetID; + + ioc->events[idx].data[1] = (sense_data[13] << 8) || sense_data[12]; + + ioc->eventContext++; + } + } + + /* Print an error report for the user. + */ + thisIo.cdbPtr = sc->cmnd; + thisIo.sensePtr = sc->sense_buffer; + thisIo.SCSIStatus = pScsiReply->SCSIStatus; + thisIo.DoDisplay = 1; + if (hd->is_multipath) + sprintf(devFoo, "%d:%d:%d \"%s\"", + hd->ioc->id, + pReq->TargetID, + pReq->LUN[1], + target->dev_vol_name); + else + sprintf(devFoo, "%d:%d:%d", hd->ioc->id, sc->target, sc->lun); + thisIo.DevIDStr = devFoo; +/* fubar */ + thisIo.dataPtr = NULL; + thisIo.inqPtr = NULL; + if (sc->device) { + thisIo.inqPtr = sc->device->vendor-8; /* FIXME!!! */ + } + (void) mpt_ScsiHost_ErrorReport(&thisIo); + + } else { + dprintk((MYIOC_s_INFO_FMT "Hmmm... SenseData len=0! (?)\n", + hd->ioc->name)); + } + + return; +} + +static u32 +SCPNT_TO_LOOKUP_IDX(Scsi_Cmnd *sc) +{ + MPT_SCSI_HOST *hd; + int i; + + hd = (MPT_SCSI_HOST *) sc->host->hostdata; + + for (i = 0; i < hd->ioc->req_depth; i++) { + if (hd->ScsiLookup[i] == sc) { + return i; + } + } + + return -1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +/* see mptscsih.h */ + +#ifdef MPT_SCSIHOST_NEED_ENTRY_EXIT_HOOKUPS + static Scsi_Host_Template driver_template = MPT_SCSIHOST; +# include "../../scsi/scsi_module.c.inc" +#endif + + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* Search the pendingQ for a command with specific index. + * If found, delete and return mf pointer + * If not found, return NULL + */ +static MPT_FRAME_HDR * +mptscsih_search_pendingQ(MPT_SCSI_HOST *hd, int scpnt_idx) +{ + unsigned long flags; + MPT_DONE_Q *buffer; + MPT_FRAME_HDR *mf = NULL; + MPT_FRAME_HDR *cmdMfPtr = NULL; + + ddvtprintk((MYIOC_s_INFO_FMT ": search_pendingQ ...", hd->ioc->name)); + cmdMfPtr = MPT_INDEX_2_MFPTR(hd->ioc, scpnt_idx); + spin_lock_irqsave(&hd->freedoneQlock, flags); + if (!Q_IS_EMPTY(&hd->pendingQ)) { + buffer = hd->pendingQ.head; + do { + mf = (MPT_FRAME_HDR *) buffer->argp; + if (mf == cmdMfPtr) { + Q_DEL_ITEM(buffer); + + /* clear the arg pointer + */ + buffer->argp = NULL; + + /* Add to the freeQ + */ + Q_ADD_TAIL(&hd->freeQ.head, buffer, MPT_DONE_Q); + break; + } + mf = NULL; + } while ((buffer = buffer->forw) != (MPT_DONE_Q *) &hd->pendingQ); + } + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + ddvtprintk((" ...return %p\n", mf)); + return mf; +} + +/* Post all commands on the pendingQ to the FW. + * Lock Q when deleting/adding members + * Lock io_request_lock for OS callback. + */ +static void +post_pendingQ_commands(MPT_SCSI_HOST *hd) +{ + MPT_FRAME_HDR *mf; + MPT_DONE_Q *buffer; + unsigned long flags; + + /* Flush the pendingQ. + */ + ddvtprintk((MYIOC_s_INFO_FMT ": post_pendingQ_commands\n", hd->ioc->name)); + while (1) { + spin_lock_irqsave(&hd->freedoneQlock, flags); + if (Q_IS_EMPTY(&hd->pendingQ)) { + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + break; + } + + buffer = hd->pendingQ.head; + /* Delete from Q + */ + Q_DEL_ITEM(buffer); + + mf = (MPT_FRAME_HDR *) buffer->argp; + buffer->argp = NULL; + + /* Add to the freeQ + */ + Q_ADD_TAIL(&hd->freeQ.head, buffer, MPT_DONE_Q); + spin_unlock_irqrestore(&hd->freedoneQlock, flags); + + if (!mf) { + /* This should never happen */ + printk(MYIOC_s_WARN_FMT "post_pendingQ_commands: mf %p\n", hd->ioc->name, (void *) mf); + continue; + } + + mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf); + +#if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY) + { + u16 req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); + Scsi_Cmnd *sc = hd->ScsiLookup[req_idx]; + printk(MYIOC_s_INFO_FMT "Issued SCSI cmd (sc=%p) idx=%d (mf=%p)\n", + hd->ioc->name, sc, req_idx, mf); + } +#endif + } + + return; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase) +{ + MPT_SCSI_HOST *hd = NULL; + unsigned long flags; + + dtmprintk((KERN_WARNING MYNAM + ": IOC %s_reset routed to SCSI host driver!\n", + reset_phase==MPT_IOC_SETUP_RESET ? "setup" : ( + reset_phase==MPT_IOC_PRE_RESET ? "pre" : "post"))); + + /* If a FW reload request arrives after base installed but + * before all scsi hosts have been attached, then an alt_ioc + * may have a NULL sh pointer. + */ + if ((ioc->sh == NULL) || (ioc->sh->hostdata == NULL)) + return 0; + else + hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; + + if (reset_phase == MPT_IOC_SETUP_RESET) { + dtmprintk((MYIOC_s_WARN_FMT "Setup-Diag Reset\n", ioc->name)); + /* Clean Up: + * 1. Set Hard Reset Pending Flag + * All new commands go to doneQ + */ + hd->resetPending = 1; + + /* 2. Reset timeouts on all running commands + */ + mptscsih_reset_timeouts (hd); + + } else if (reset_phase == MPT_IOC_PRE_RESET) { + dtmprintk((MYIOC_s_WARN_FMT "Pre-Diag Reset\n", ioc->name)); + + /* 2. Flush running commands + * Clean ScsiLookup (and associated memory) + * AND clean mytaskQ + */ + + /* 2b. Reply to OS all known outstanding I/O commands. + */ + mptscsih_flush_running_cmds(hd); + + /* 2c. If there was an internal command that + * has not completed, configuration or io request, + * free these resources. + */ + if (hd->cmdPtr) { + del_timer(&hd->timer); + mpt_free_msg_frame(ScsiScanDvCtx, ioc->id, hd->cmdPtr); + atomic_dec(&queue_depth); + } + + /* 2d. If a task management has not completed, + * free resources associated with this request. + */ + if (hd->tmPtr) { + del_timer(&hd->TMtimer); + mpt_free_msg_frame(ScsiTaskCtx, ioc->id, hd->tmPtr); + } + +#ifndef MPT_SCSI_USE_NEW_EH + /* 2e. Delete all commands on taskQ + * Should be superfluous - as this taskQ should + * be empty. + */ + clean_taskQ(hd); +#endif + +#ifdef MPTSCSIH_DBG_TIMEOUT + ioc->timeout_hard = 0; +#endif + + dtmprintk((MYIOC_s_WARN_FMT "Pre-Reset complete.\n", ioc->name)); + } else { + ScsiCfgData *pSpi = NULL; + + dtmprintk((MYIOC_s_WARN_FMT "Post-Diag Reset\n", ioc->name)); + + /* Once a FW reload begins, all new OS commands are + * redirected to the doneQ w/ a reset status. + * Init all control structures. + */ + + /* ScsiLookup initialization + */ + { + int ii; + for (ii=0; ii < hd->ioc->req_depth; ii++) + hd->ScsiLookup[ii] = NULL; + } + + /* 2. Chain Buffer initialization + */ + mptscsih_initChainBuffers(hd, 0); + + /* 3. tmPtr clear + */ + if (hd->tmPtr) { + hd->tmPtr = NULL; + } + + /* 4. Renegotiate to all devices, if SCSI + */ + if (hd->is_spi) + mptscsih_writeSDP1(hd, 0, 0, MPT_SCSICFG_ALL_IDS | MPT_SCSICFG_USE_NVRAM); + + /* 5. Enable new commands to be posted + */ + spin_lock_irqsave(&ioc->FreeQlock, flags); + hd->tmPending = 0; + spin_unlock_irqrestore(&ioc->FreeQlock, flags); + hd->resetPending = 0; + hd->numTMrequests = 0; +#ifdef MPT_SCSI_USE_NEW_EH + hd->tmState = TM_STATE_NONE; +#endif + + /* 6. If there was an internal command, + * wake this process up. + */ + if (hd->cmdPtr) { + /* + * Wake up the original calling thread + */ + hd->pLocal = &hd->localReply; + hd->pLocal->completion = MPT_SCANDV_DID_RESET; + scandv_wait_done = 1; +/* SAE: No wait queues */ +#ifdef XENO_KILLED + wake_up(&scandv_waitq); +#else + mdelay(100); +#endif + hd->cmdPtr = NULL; + } + + /* 7. Flush doneQ + */ + flush_doneQ(hd); + + /* 8. Set flag to force DV and re-read IOC Page 3 + */ + if (hd->is_spi) { + pSpi = &ioc->spi_data; + pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3; + ddvtprintk(("Set reload IOC Pg3 Flag\n")); + } + + dtmprintk((MYIOC_s_WARN_FMT "Post-Reset complete.\n", ioc->name)); + } + + return 1; /* currently means nothing really */ +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply) +{ + MPT_SCSI_HOST *hd; + u8 event = le32_to_cpu(pEvReply->Event) & 0xFF; + + dprintk((MYIOC_s_INFO_FMT "MPT event (=%02Xh) routed to SCSI host driver!\n", ioc->name, event)); + + switch (event) { + case MPI_EVENT_UNIT_ATTENTION: /* 03 */ + /* FIXME! */ + break; + case MPI_EVENT_IOC_BUS_RESET: /* 04 */ + case MPI_EVENT_EXT_BUS_RESET: /* 05 */ + hd = NULL; + if (ioc->sh) { + hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; + if (hd && (hd->is_spi) && (hd->soft_resets < -1)) + hd->soft_resets++; + } + break; + case MPI_EVENT_LOGOUT: /* 09 */ + /* FIXME! */ + break; + + /* + * CHECKME! Don't think we need to do + * anything for these, but... + */ + case MPI_EVENT_RESCAN: /* 06 */ + case MPI_EVENT_LINK_STATUS_CHANGE: /* 07 */ + case MPI_EVENT_LOOP_STATE_CHANGE: /* 08 */ + /* + * CHECKME! Falling thru... + */ + break; + + case MPI_EVENT_INTEGRATED_RAID: /* 0B */ +#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION + /* negoNvram set to 0 if DV enabled and to USE_NVRAM if + * if DV disabled. Need to check for target mode. + */ + hd = NULL; + if (ioc->sh) + hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; + + if (hd && (hd->is_spi) && (hd->negoNvram == 0)) { + ScsiCfgData *pSpi; + Ioc3PhysDisk_t *pPDisk; + int numPDisk; + u8 reason; + u8 physDiskNum; + + reason = (le32_to_cpu(pEvReply->Data[0]) & 0x00FF0000) >> 16; + if (reason == MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED) { + /* New or replaced disk. + * Set DV flag and schedule DV. + */ + pSpi = &ioc->spi_data; + physDiskNum = (le32_to_cpu(pEvReply->Data[0]) & 0xFF000000) >> 24; + ddvtprintk(("DV requested for phys disk id %d\n", physDiskNum)); + if (pSpi->pIocPg3) { + pPDisk = pSpi->pIocPg3->PhysDisk; + numPDisk =pSpi->pIocPg3->NumPhysDisks; + + while (numPDisk) { + if (physDiskNum == pPDisk->PhysDiskNum) { + pSpi->dvStatus[pPDisk->PhysDiskID] = (MPT_SCSICFG_NEED_DV | MPT_SCSICFG_DV_NOT_DONE); + pSpi->forceDv = MPT_SCSICFG_NEED_DV; + ddvtprintk(("NEED_DV set for phys disk id %d\n", pPDisk->PhysDiskID)); + break; + } + pPDisk++; + numPDisk--; + } + + if (numPDisk == 0) { + /* The physical disk that needs DV was not found + * in the stored IOC Page 3. The driver must reload + * this page. DV routine will set the NEED_DV flag for + * all phys disks that have DV_NOT_DONE set. + */ + pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3; + ddvtprintk(("phys disk %d not found. Setting reload IOC Pg3 Flag\n", physDiskNum)); + } + } + } + } +#endif + +#if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY) + printk("Raid Event RF: "); + { + u32 *m = (u32 *)pEvReply; + int ii; + int n = (int)pEvReply->MsgLength; + for (ii=6; ii < n; ii++) + printk(" %08x", le32_to_cpu(m[ii])); + printk("\n"); + } +#endif + break; + + case MPI_EVENT_NONE: /* 00 */ + case MPI_EVENT_LOG_DATA: /* 01 */ + case MPI_EVENT_STATE_CHANGE: /* 02 */ + case MPI_EVENT_EVENT_CHANGE: /* 0A */ + default: + dprintk((KERN_INFO " Ignoring event (=%02Xh)\n", event)); + break; + } + + return 1; /* currently means nothing really */ +} + +#if 0 /* { */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * scsiherr.c - Fusion MPT SCSI Host driver error handling/reporting. + * + * drivers/message/fusion/scsiherr.c + */ + +//extern const char **mpt_ScsiOpcodesPtr; /* needed by mptscsih.c */ +//extern ASCQ_Table_t *mpt_ASCQ_TablePtr; +//extern int mpt_ASCQ_TableSz; + +#define MYNAM "mptscsih" + +#endif /* } */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Private data... + */ +static ASCQ_Table_t *mptscsih_ASCQ_TablePtr; + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* old symsense.c stuff... */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Private data... + * To protect ourselves against those that would pass us bogus pointers + */ +static u8 dummyInqData[SCSI_STD_INQUIRY_BYTES] + = { 0x1F, 0x00, 0x00, 0x00, + 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static u8 dummySenseData[SCSI_STD_SENSE_BYTES] + = { 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 }; +static u8 dummyCDB[16] + = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static u8 dummyScsiData[16] + = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +#if 0 +static const char *PeripheralDeviceTypeString[32] = { + "Direct-access", /* 00h */ + "Sequential-access", /* 01h */ + "Printer", /* 02h */ + "Processor", /* 03h */ + /*"Write-Once-Read-Multiple",*/ /* 04h */ + "WORM", /* 04h */ + "CD-ROM", /* 05h */ + "Scanner", /* 06h */ + "Optical memory", /* 07h */ + "Media Changer", /* 08h */ + "Communications", /* 09h */ + "(Graphics arts pre-press)", /* 0Ah */ + "(Graphics arts pre-press)", /* 0Bh */ + "Array controller", /* 0Ch */ + "Enclosure services", /* 0Dh */ + "Simplified direct-access", /* 0Eh */ + "Reserved-0Fh", /* 0Fh */ + "Reserved-10h", /* 10h */ + "Reserved-11h", /* 11h */ + "Reserved-12h", /* 12h */ + "Reserved-13h", /* 13h */ + "Reserved-14h", /* 14h */ + "Reserved-15h", /* 15h */ + "Reserved-16h", /* 16h */ + "Reserved-17h", /* 17h */ + "Reserved-18h", /* 18h */ + "Reserved-19h", /* 19h */ + "Reserved-1Ah", /* 1Ah */ + "Reserved-1Bh", /* 1Bh */ + "Reserved-1Ch", /* 1Ch */ + "Reserved-1Dh", /* 1Dh */ + "Reserved-1Eh", /* 1Eh */ + "Unknown" /* 1Fh */ +}; +#endif + +static char *ScsiStatusString[] = { + "GOOD", /* 00h */ + NULL, /* 01h */ + "CHECK CONDITION", /* 02h */ + NULL, /* 03h */ + "CONDITION MET", /* 04h */ + NULL, /* 05h */ + NULL, /* 06h */ + NULL, /* 07h */ + "BUSY", /* 08h */ + NULL, /* 09h */ + NULL, /* 0Ah */ + NULL, /* 0Bh */ + NULL, /* 0Ch */ + NULL, /* 0Dh */ + NULL, /* 0Eh */ + NULL, /* 0Fh */ + "INTERMEDIATE", /* 10h */ + NULL, /* 11h */ + NULL, /* 12h */ + NULL, /* 13h */ + "INTERMEDIATE-CONDITION MET", /* 14h */ + NULL, /* 15h */ + NULL, /* 16h */ + NULL, /* 17h */ + "RESERVATION CONFLICT", /* 18h */ + NULL, /* 19h */ + NULL, /* 1Ah */ + NULL, /* 1Bh */ + NULL, /* 1Ch */ + NULL, /* 1Dh */ + NULL, /* 1Eh */ + NULL, /* 1Fh */ + NULL, /* 20h */ + NULL, /* 21h */ + "COMMAND TERMINATED", /* 22h */ + NULL, /* 23h */ + NULL, /* 24h */ + NULL, /* 25h */ + NULL, /* 26h */ + NULL, /* 27h */ + "TASK SET FULL", /* 28h */ + NULL, /* 29h */ + NULL, /* 2Ah */ + NULL, /* 2Bh */ + NULL, /* 2Ch */ + NULL, /* 2Dh */ + NULL, /* 2Eh */ + NULL, /* 2Fh */ + "ACA ACTIVE", /* 30h */ + NULL +}; + +static const char *ScsiCommonOpString[] = { + "TEST UNIT READY", /* 00h */ + "REZERO UNIT (REWIND)", /* 01h */ + NULL, /* 02h */ + "REQUEST_SENSE", /* 03h */ + "FORMAT UNIT (MEDIUM)", /* 04h */ + "READ BLOCK LIMITS", /* 05h */ + NULL, /* 06h */ + "REASSIGN BLOCKS", /* 07h */ + "READ(6)", /* 08h */ + NULL, /* 09h */ + "WRITE(6)", /* 0Ah */ + "SEEK(6)", /* 0Bh */ + NULL, /* 0Ch */ + NULL, /* 0Dh */ + NULL, /* 0Eh */ + "READ REVERSE", /* 0Fh */ + "WRITE_FILEMARKS", /* 10h */ + "SPACE(6)", /* 11h */ + "INQUIRY", /* 12h */ + NULL +}; + +static const char *SenseKeyString[] = { + "NO SENSE", /* 0h */ + "RECOVERED ERROR", /* 1h */ + "NOT READY", /* 2h */ + "MEDIUM ERROR", /* 3h */ + "HARDWARE ERROR", /* 4h */ + "ILLEGAL REQUEST", /* 5h */ + "UNIT ATTENTION", /* 6h */ + "DATA PROTECT", /* 7h */ + "BLANK CHECK", /* 8h */ + "VENDOR-SPECIFIC", /* 9h */ + "ABORTED COPY", /* Ah */ + "ABORTED COMMAND", /* Bh */ + "EQUAL (obsolete)", /* Ch */ + "VOLUME OVERFLOW", /* Dh */ + "MISCOMPARE", /* Eh */ + "RESERVED", /* Fh */ + NULL +}; + +#define SPECIAL_ASCQ(c,q) \ + (((c) == 0x40 && (q) != 0x00) || ((c) == 0x4D) || ((c) == 0x70)) + +#if 0 +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Sense_Key_Specific() - If Sense_Key_Specific_Valid bit is set, + * then print additional information via + * a call to SDMS_SystemAlert(). + */ +static void Sense_Key_Specific(IO_Info_t *ioop, char *msg1) +{ + u8 *sd; + u8 BadValue; + u8 SenseKey; + int Offset; + int len = strlen(msg1); + + sd = ioop->sensePtr; + if (SD_Additional_Sense_Length(sd) < 8) + return; + + SenseKey = SD_Sense_Key(sd); + + if (SD_Sense_Key_Specific_Valid(sd)) { + if (SenseKey == SK_ILLEGAL_REQUEST) { + Offset = SD_Bad_Byte(sd); + if (SD_Was_Illegal_Request(sd)) { + BadValue = ioop->cdbPtr[Offset]; + len += sprintf(msg1+len, "\n Illegal CDB value=%02Xh found at CDB ", + BadValue); + } else { + BadValue = ioop->dataPtr[Offset]; + len += sprintf(msg1+len, "\n Illegal DATA value=%02Xh found at DATA ", + BadValue); + } + len += sprintf(msg1+len, "byte=%02Xh", Offset); + if (SD_SKS_Bit_Pointer_Valid(sd)) + len += sprintf(msg1+len, "/bit=%1Xh", SD_SKS_Bit_Pointer(sd)); + } else if ((SenseKey == SK_RECOVERED_ERROR) || + (SenseKey == SK_HARDWARE_ERROR) || + (SenseKey == SK_MEDIUM_ERROR)) { + len += sprintf(msg1+len, "\n Recovery algorithm Actual_Retry_Count=%02Xh", + SD_Actual_Retry_Count(sd)); + } + } +} +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int dump_cdb(char *foo, unsigned char *cdb) +{ + int i, grpCode, cdbLen; + int l = 0; + + grpCode = cdb[0] >> 5; + if (grpCode < 1) + cdbLen = 6; + else if (grpCode < 3) + cdbLen = 10; + else if (grpCode == 5) + cdbLen = 12; + else + cdbLen = 16; + + for (i=0; i < cdbLen; i++) + l += sprintf(foo+l, " %02X", cdb[i]); + + return l; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +#if 0 +static int dump_sd(char *foo, unsigned char *sd) +{ + int snsLen = 8 + SD_Additional_Sense_Length(sd); + int l = 0; + int i; + + for (i=0; i < MIN(snsLen,18); i++) + l += sprintf(foo+l, " %02X", sd[i]); + l += sprintf(foo+l, "%s", snsLen>18 ? " ..." : ""); + + return l; +} +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* Do ASC/ASCQ lookup/grindage to English readable string(s) */ +static const char * ascq_set_strings_4max( + u8 ASC, u8 ASCQ, + const char **s1, const char **s2, const char **s3, const char **s4) +{ + static const char *asc_04_part1_string = "LOGICAL UNIT "; + static const char *asc_04_part2a_string = "NOT READY, "; + static const char *asc_04_part2b_string = "IS "; + static const char *asc_04_ascq_NN_part3_strings[] = { /* ASC ASCQ (hex) */ + "CAUSE NOT REPORTABLE", /* 04 00 */ + "IN PROCESS OF BECOMING READY", /* 04 01 */ + "INITIALIZING CMD. REQUIRED", /* 04 02 */ + "MANUAL INTERVENTION REQUIRED", /* 04 03 */ + /* Add " IN PROGRESS" to all the following... */ + "FORMAT", /* 04 04 */ + "REBUILD", /* 04 05 */ + "RECALCULATION", /* 04 06 */ + "OPERATION", /* 04 07 */ + "LONG WRITE", /* 04 08 */ + "SELF-TEST", /* 04 09 */ + NULL + }; + static char *asc_04_part4_string = " IN PROGRESS"; + + static char *asc_29_ascq_NN_strings[] = { /* ASC ASCQ (hex) */ + "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED", /* 29 00 */ + "POWER ON OCCURRED", /* 29 01 */ + "SCSI BUS RESET OCCURRED", /* 29 02 */ + "BUS DEVICE RESET FUNCTION OCCURRED", /* 29 03 */ + "DEVICE INTERNAL RESET", /* 29 04 */ + "TRANSCEIVER MODE CHANGED TO SINGLE-ENDED", /* 29 05 */ + "TRANSCEIVER MODE CHANGED TO LVD", /* 29 06 */ + NULL + }; + static char *ascq_vendor_uniq = "(Vendor Unique)"; + static char *ascq_noone = "(no matching ASC/ASCQ description found)"; + int idx; + + *s1 = *s2 = *s3 = *s4 = ""; /* set'em all to the empty "" string */ + + /* CHECKME! Need lock/sem? + * Update and examine for isense module presense. + */ + mptscsih_ASCQ_TablePtr = (ASCQ_Table_t *)mpt_v_ASCQ_TablePtr; + + if (mptscsih_ASCQ_TablePtr == NULL) { + /* 2nd chances... */ + if (ASC == 0x04 && (ASCQ < sizeof(asc_04_ascq_NN_part3_strings)/sizeof(char*)-1)) { + *s1 = asc_04_part1_string; + *s2 = (ASCQ == 0x01) ? asc_04_part2b_string : asc_04_part2a_string; + *s3 = asc_04_ascq_NN_part3_strings[ASCQ]; + /* check for " IN PROGRESS" ones */ + if (ASCQ >= 0x04) + *s4 = asc_04_part4_string; + } else if (ASC == 0x29 && (ASCQ < sizeof(asc_29_ascq_NN_strings)/sizeof(char*)-1)) + *s1 = asc_29_ascq_NN_strings[ASCQ]; + /* + * Else { leave all *s[1-4] values pointing to the empty "" string } + */ + return *s1; + } + + /* + * Need to check ASC here; if it is "special," then + * the ASCQ is variable, and indicates failed component number. + * We must treat the ASCQ as a "don't care" while searching the + * mptscsih_ASCQ_Table[] by masking it off, and then restoring it later + * on when we actually need to identify the failed component. + */ + if (SPECIAL_ASCQ(ASC,ASCQ)) + ASCQ = 0xFF; + + /* OK, now search mptscsih_ASCQ_Table[] for a matching entry */ + for (idx = 0; mptscsih_ASCQ_TablePtr && idx < mpt_ASCQ_TableSz; idx++) + if ((ASC == mptscsih_ASCQ_TablePtr[idx].ASC) && (ASCQ == mptscsih_ASCQ_TablePtr[idx].ASCQ)) { + *s1 = mptscsih_ASCQ_TablePtr[idx].Description; + return *s1; + } + + if ((ASC >= 0x80) || (ASCQ >= 0x80)) + *s1 = ascq_vendor_uniq; + else + *s1 = ascq_noone; + + return *s1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * SCSI Information Report; desired output format... + *--- +SCSI Error: (iocnum:target_id:LUN) Status=02h (CHECK CONDITION) + Key=6h (UNIT ATTENTION); FRU=03h + ASC/ASCQ=29h/00h, "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED" + CDB: 00 00 00 00 00 00 - TestUnitReady + *--- + */ +/* + * SCSI Error Report; desired output format... + *--- +SCSI Error Report =-=-=-=-=-=-=-=-=-=-=-=-=-= (ioc0,scsi0:0) + SCSI_Status=02h (CHECK CONDITION) + Original_CDB[]: 00 00 00 00 00 00 - TestUnitReady + SenseData[12h]: 70 00 06 00 00 00 00 0A 00 00 00 00 29 00 03 00 00 00 + SenseKey=6h (UNIT ATTENTION); FRU=03h + ASC/ASCQ=29h/00h, "POWER ON, RESET, OR BUS DEVICE RESET OCCURRED" + *--- + */ + +int mpt_ScsiHost_ErrorReport(IO_Info_t *ioop) +{ + char foo[512]; + char buf2[32]; + char *statstr; + const char *opstr; + int sk = SD_Sense_Key(ioop->sensePtr); + const char *skstr = SenseKeyString[sk]; + unsigned char asc = SD_ASC(ioop->sensePtr); + unsigned char ascq = SD_ASCQ(ioop->sensePtr); + int l; + + /* Change the error logging to only report errors on + * read and write commands. Ignore errors on other commands. + * Should this be configurable via proc? + */ + switch (ioop->cdbPtr[0]) { + case READ_6: + case WRITE_6: + case READ_10: + case WRITE_10: + case READ_12: + case WRITE_12: + break; + default: + return 0; + } + + /* + * More quiet mode. + * Filter out common, repetitive, warning-type errors... like: + * POWER ON (06,29/00 or 06,29/01), + * SPINNING UP (02,04/01), + * LOGICAL UNIT NOT SUPPORTED (05,25/00), etc. + */ + if (sk == SK_NO_SENSE) { + return 0; + } + + if ( (sk==SK_UNIT_ATTENTION && asc==0x29 && (ascq==0x00 || ascq==0x01)) + || (sk==SK_NOT_READY && asc==0x04 && (ascq==0x01 || ascq==0x02)) + || (sk==SK_ILLEGAL_REQUEST && asc==0x25 && ascq==0x00) + ) + { + /* Do nothing! */ + return 0; + } + + /* Prevent the system from continually writing to the log + * if a medium is not found: 02 3A 00 + * Changer issues: TUR, Read Capacity, Table of Contents continually + */ + if (sk==SK_NOT_READY && asc==0x3A) { + if (ioop->cdbPtr == NULL) { + return 0; + } else if ((ioop->cdbPtr[0] == CMD_TestUnitReady) || + (ioop->cdbPtr[0] == CMD_ReadCapacity) || + (ioop->cdbPtr[0] == 0x43)) { + return 0; + } + } + if (sk==SK_UNIT_ATTENTION) { + if (ioop->cdbPtr == NULL) + return 0; + else if (ioop->cdbPtr[0] == CMD_TestUnitReady) + return 0; + } + + /* + * Protect ourselves... + */ + if (ioop->cdbPtr == NULL) + ioop->cdbPtr = dummyCDB; + if (ioop->sensePtr == NULL) + ioop->sensePtr = dummySenseData; + if (ioop->inqPtr == NULL) + ioop->inqPtr = dummyInqData; + if (ioop->dataPtr == NULL) + ioop->dataPtr = dummyScsiData; + + statstr = NULL; + if ((ioop->SCSIStatus >= sizeof(ScsiStatusString)/sizeof(char*)-1) || + ((statstr = (char*)ScsiStatusString[ioop->SCSIStatus]) == NULL)) { + (void) sprintf(buf2, "Bad-Reserved-%02Xh", ioop->SCSIStatus); + statstr = buf2; + } + + opstr = NULL; + if (1+ioop->cdbPtr[0] <= sizeof(ScsiCommonOpString)/sizeof(char*)) + opstr = ScsiCommonOpString[ioop->cdbPtr[0]]; + else if (mpt_ScsiOpcodesPtr) + opstr = mpt_ScsiOpcodesPtr[ioop->cdbPtr[0]]; + + l = sprintf(foo, "SCSI Error: (%s) Status=%02Xh (%s)\n", + ioop->DevIDStr, + ioop->SCSIStatus, + statstr); + l += sprintf(foo+l, " Key=%Xh (%s); FRU=%02Xh\n ASC/ASCQ=%02Xh/%02Xh", + sk, skstr, SD_FRU(ioop->sensePtr), asc, ascq ); + { + const char *x1, *x2, *x3, *x4; + x1 = x2 = x3 = x4 = ""; + x1 = ascq_set_strings_4max(asc, ascq, &x1, &x2, &x3, &x4); + if (x1 != NULL) { + if (x1[0] != '(') + l += sprintf(foo+l, " \"%s%s%s%s\"", x1,x2,x3,x4); + else + l += sprintf(foo+l, " %s%s%s%s", x1,x2,x3,x4); + } + } + l += sprintf(foo+l, "\n CDB:"); + l += dump_cdb(foo+l, ioop->cdbPtr); + if (opstr) + l += sprintf(foo+l, " - \"%s\"", opstr); + l += sprintf(foo+l, "\n"); + + PrintF(("%s\n", foo)); + + return l; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_initTarget - Target, LUN alloc/free functionality. + * @hd: Pointer to MPT_SCSI_HOST structure + * @bus_id: Bus number (?) + * @target_id: SCSI target id + * @lun: SCSI LUN id + * @data: Pointer to data + * @dlen: Number of INQUIRY bytes + * + * NOTE: It's only SAFE to call this routine if data points to + * sane & valid STANDARD INQUIRY data! + * + * Allocate and initialize memory for this target. + * Save inquiry data. + * + * Returns pointer to VirtDevice structure. + */ +static VirtDevice * +mptscsih_initTarget(MPT_SCSI_HOST *hd, int bus_id, int target_id, u8 lun, char *data, int dlen) +{ + VirtDevice *vdev; + int sz; + + dprintk((MYIOC_s_INFO_FMT "initTarget (%d,%d,%d) called, hd=%p\n", + hd->ioc->name, bus_id, target_id, lun, hd)); + + if ((vdev = hd->Targets[target_id]) == NULL) { + if ((vdev = kmalloc(sizeof(VirtDevice), GFP_ATOMIC)) == NULL) { + printk(MYIOC_s_ERR_FMT "initTarget kmalloc(%d) FAILED!\n", + hd->ioc->name, (int)sizeof(VirtDevice)); + } else { + memset(vdev, 0, sizeof(VirtDevice)); + rwlock_init(&vdev->VdevLock); + Q_INIT(&vdev->WaitQ, void); + Q_INIT(&vdev->SentQ, void); + Q_INIT(&vdev->DoneQ, void); + vdev->tflags = 0; + vdev->ioc_id = hd->ioc->id; + vdev->target_id = target_id; + vdev->bus_id = bus_id; + + hd->Targets[target_id] = vdev; + dprintk((KERN_INFO " *NEW* Target structure (id %d) @ %p\n", + target_id, vdev)); + } + } + + vdev->raidVolume = 0; + if (vdev && hd->is_spi) { + if (hd->ioc->spi_data.isRaid & (1 << target_id)) { + vdev->raidVolume = 1; + ddvtprintk((KERN_INFO "RAID Volume @ id %d\n", target_id)); + } + } + + if (vdev && data) { + if ((!(vdev->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) || + ((dlen > 56) && (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_56)))) { + + /* Copy the inquiry data - if we haven't yet. + */ + sz = MIN(dlen, SCSI_STD_INQUIRY_BYTES); + + memcpy (vdev->inq_data, data, sz); + vdev->tflags |= MPT_TARGET_FLAGS_VALID_INQUIRY; + + /* Update the target capabilities + */ + if (dlen > 56) { + mptscsih_setTargetNegoParms(hd, vdev, data[56]); + vdev->tflags |= MPT_TARGET_FLAGS_VALID_56; + } else + mptscsih_setTargetNegoParms(hd, vdev, 0); + + /* If LUN 0, tape and have not done DV, set the DV flag. + */ + if (hd->is_spi && (lun == 0) && ((data[0] & 0x1F) == 0x01)) { + ScsiCfgData *pSpi = &hd->ioc->spi_data; + if (pSpi->dvStatus[target_id] & MPT_SCSICFG_DV_NOT_DONE) + pSpi->dvStatus[target_id] |= MPT_SCSICFG_NEED_DV; + } + } + + /* Is LUN supported? If so, upper 3 bits will be 0 + * in first byte of inquiry data. + */ + if ((*data & 0xe0) == 0) + vdev->luns |= (1 << lun); + } + + + dprintk((KERN_INFO " target = %p\n", vdev)); + return vdev; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Update the target negotiation parameters based on the + * the Inquiry data, adapter capabilities, and NVRAM settings. + * + */ +void mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56) +{ + ScsiCfgData *pspi_data = &hd->ioc->spi_data; + int id = (int) target->target_id; + int nvram; + char canQ = 0; + u8 width = MPT_NARROW; + u8 factor = MPT_ASYNC; + u8 offset = 0; + u8 version, nfactor; + u8 noQas = 0; + + ddvtprintk((KERN_INFO "set Target: (id %d) byte56 0x%x\n", id, byte56)); + + if (!hd->is_spi) { + if (target->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) { + if (target->inq_data[7] & 0x02) + target->tflags |= MPT_TARGET_FLAGS_Q_YES; + } + return; + } + + /* noQas == 0 => device supports QAS. Need byte 56 of Inq to determine + * support. If available, default QAS to off and allow enabling. + * If not available, default QAS to on, turn off for non-disks. + */ + if (target->tflags & MPT_TARGET_FLAGS_VALID_56) + noQas = 1; + + /* Set flags based on Inquiry data + */ + if (target->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY) { + if ((target->inq_data[0] & 0x1F) != 0x00) + noQas = 1; + + version = target->inq_data[2] & 0x07; + if (version < 2) { + width = 0; + factor = MPT_ULTRA2; + offset = pspi_data->maxSyncOffset; + } else { + if (target->inq_data[7] & 0x20) { + width = 1; + } + + if (target->inq_data[7] & 0x10) { + /* bits 2 & 3 show DT support + */ + if ((byte56 & 0x04) == 0) + factor = MPT_ULTRA2; + else if ((byte56 & 0x03) == 0) + factor = MPT_ULTRA160; + else + factor = MPT_ULTRA320; + + /* If RAID, never disable QAS + * else if non RAID, do not disable + * QAS if bit 1 is set + * bit 1 QAS support, non-raid only + * bit 0 IU support + */ + if ((target->raidVolume == 1) || ((byte56 & 0x02) != 0)) + noQas = 0; + + offset = pspi_data->maxSyncOffset; + } else { + factor = MPT_ASYNC; + offset = 0; + } + } + + if (target->inq_data[7] & 0x02) { + canQ = 1; + } + + /* Update tflags based on NVRAM settings. (SCSI only) + */ + if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) { + nvram = pspi_data->nvram[id]; + nfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8; + + if (width) + width = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1; + + if (offset > 0) { + /* Ensure factor is set to the + * maximum of: adapter, nvram, inquiry + */ + if (nfactor) { + if (nfactor < pspi_data->minSyncFactor ) + nfactor = pspi_data->minSyncFactor; + + factor = MAX (factor, nfactor); + if (factor == MPT_ASYNC) + offset = 0; + } else { + offset = 0; + factor = MPT_ASYNC; + } + } else { + factor = MPT_ASYNC; + } + } + + /* Make sure data is consistent + */ + if ((!width) && (factor < MPT_ULTRA2)) { + factor = MPT_ULTRA2; + } + + /* Save the data to the target structure. + */ + target->minSyncFactor = factor; + target->maxOffset = offset; + target->maxWidth = width; + if (canQ) { + target->tflags |= MPT_TARGET_FLAGS_Q_YES; + } + + target->tflags |= MPT_TARGET_FLAGS_VALID_NEGO; + + /* Disable unused features. + */ + target->negoFlags = pspi_data->noQas; + if (!width) + target->negoFlags |= MPT_TARGET_NO_NEGO_WIDE; + + if (!offset) + target->negoFlags |= MPT_TARGET_NO_NEGO_SYNC; + + if (noQas) + target->negoFlags |= MPT_TARGET_NO_NEGO_QAS; + + /* GEM, processor WORKAROUND + */ + if (((target->inq_data[0] & 0x1F) == 0x03) || ((target->inq_data[0] & 0x1F) > 0x08)){ + target->negoFlags |= (MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC); + pspi_data->dvStatus[id] |= MPT_SCSICFG_BLK_NEGO; + } + + /* Disable QAS if mixed configuration case + */ + if ((noQas) && (!pspi_data->noQas) && ((target->inq_data[0] & 0x1F) != 0x03)){ + VirtDevice *vdev; + int ii; + + ddvtprintk((KERN_INFO "Disabling QAS!\n")); + pspi_data->noQas = MPT_TARGET_NO_NEGO_QAS; + for (ii = 0; ii < id; ii++) { + vdev = hd->Targets[id]; + if (vdev != NULL) + vdev->negoFlags |= MPT_TARGET_NO_NEGO_QAS; + } + } + } + + return; +} + +/* If DV disabled (negoNvram set to USE_NVARM) or if not LUN 0, return. + * Else set the NEED_DV flag after Read Capacity Issued (disks) + * or Mode Sense (cdroms). + * + * Tapes, initTarget will set this flag on completion of Inquiry command. + * Called only if DV_NOT_DONE flag is set + */ +static void mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq) +{ + u8 cmd; + + if ((pReq->LUN[1] != 0) || (hd->negoNvram != 0)) + return; + + cmd = pReq->CDB[0]; + + if ((cmd == READ_CAPACITY) || (cmd == MODE_SENSE)) { + ScsiCfgData *pSpi = &hd->ioc->spi_data; + if ((pSpi->isRaid & (1 << pReq->TargetID)) && pSpi->pIocPg3) { + /* Set NEED_DV for all hidden disks + */ + Ioc3PhysDisk_t *pPDisk = pSpi->pIocPg3->PhysDisk; + int numPDisk = pSpi->pIocPg3->NumPhysDisks; + + while (numPDisk) { + pSpi->dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV; + ddvtprintk(("NEED_DV set for phys disk id %d\n", pPDisk->PhysDiskID)); + pPDisk++; + numPDisk--; + } + } + pSpi->dvStatus[pReq->TargetID] |= MPT_SCSICFG_NEED_DV; + ddvtprintk(("NEED_DV set for visible disk id %d\n", pReq->TargetID)); + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * If no Target, bus reset on 1st I/O. Set the flag to + * prevent any future negotiations to this device. + */ +static void mptscsih_no_negotiate(MPT_SCSI_HOST *hd, int target_id) +{ + + if ((hd->Targets) && (hd->Targets[target_id] == NULL)) + hd->ioc->spi_data.dvStatus[target_id] |= MPT_SCSICFG_BLK_NEGO; + + return; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * SCSI Config Page functionality ... + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* mptscsih_setDevicePage1Flags - add Requested and Configuration fields flags + * based on width, factor and offset parameters. + * @width: bus width + * @factor: sync factor + * @offset: sync offset + * @requestedPtr: pointer to requested values (updated) + * @configurationPtr: pointer to configuration values (updated) + * @flags: flags to block WDTR or SDTR negotiation + * + * Return: None. + * + * Remark: Called by writeSDP1 and _dv_params + */ +static void +mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags) +{ + u8 nowide = flags & MPT_TARGET_NO_NEGO_WIDE; + u8 nosync = flags & MPT_TARGET_NO_NEGO_SYNC; + + *configurationPtr = 0; + *requestedPtr = width ? MPI_SCSIDEVPAGE1_RP_WIDE : 0; + *requestedPtr |= (offset << 16) | (factor << 8); + + if (width && offset && !nowide && !nosync) { + if (factor < MPT_ULTRA160) { + *requestedPtr |= (MPI_SCSIDEVPAGE1_RP_IU + MPI_SCSIDEVPAGE1_RP_DT); + if ((flags & MPT_TARGET_NO_NEGO_QAS) == 0) + *requestedPtr |= MPI_SCSIDEVPAGE1_RP_QAS; + } else if (factor < MPT_ULTRA2) { + *requestedPtr |= MPI_SCSIDEVPAGE1_RP_DT; + } + } + + if (nowide) + *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_WDTR_DISALLOWED; + + if (nosync) + *configurationPtr |= MPI_SCSIDEVPAGE1_CONF_SDTR_DISALLOWED; + + return; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* mptscsih_writeSDP1 - write SCSI Device Page 1 + * @hd: Pointer to a SCSI Host Strucutre + * @portnum: IOC port number + * @target_id: writeSDP1 for single ID + * @flags: MPT_SCSICFG_ALL_IDS, MPT_SCSICFG_USE_NVRAM, MPT_SCSICFG_BLK_NEGO + * + * Return: -EFAULT if read of config page header fails + * or 0 if success. + * + * Remark: If a target has been found, the settings from the + * target structure are used, else the device is set + * to async/narrow. + * + * Remark: Called during init and after a FW reload. + * Remark: We do not wait for a return, write pages sequentially. + */ +static int +mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target_id, int flags) +{ + MPT_ADAPTER *ioc = hd->ioc; + Config_t *pReq = NULL; + SCSIDevicePage1_t *pData = NULL; + VirtDevice *pTarget = NULL; + MPT_FRAME_HDR *mf; + dma_addr_t dataDma; + u16 req_idx; + u32 frameOffset; + u32 requested, configuration, flagsLength; + int ii, nvram; + int id = 0, maxid = 0; + u8 width; + u8 factor; + u8 offset; + u8 bus = 0; + u8 negoFlags; + u8 maxwidth, maxoffset, maxfactor; + + + if (ioc->spi_data.sdp1length == 0) + return 0; + + if (flags & MPT_SCSICFG_ALL_IDS) { + id = 0; + maxid = ioc->sh->max_id - 1; + } else if (ioc->sh) { + id = target_id; + maxid = MIN(id, ioc->sh->max_id - 1); + } + + for (; id <= maxid; id++) { + + if (id == ioc->pfacts[portnum].PortSCSIID) + continue; + + /* Use NVRAM to get adapter and target maximums + * Data over-riden by target structure information, if present + */ + maxwidth = ioc->spi_data.maxBusWidth; + maxoffset = ioc->spi_data.maxSyncOffset; + maxfactor = ioc->spi_data.minSyncFactor; + if (ioc->spi_data.nvram && (ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) { + nvram = ioc->spi_data.nvram[id]; + + if (maxwidth) + maxwidth = nvram & MPT_NVRAM_WIDE_DISABLE ? 0 : 1; + + if (maxoffset > 0) { + maxfactor = (nvram & MPT_NVRAM_SYNC_MASK) >> 8; + if (maxfactor == 0) { + /* Key for async */ + maxfactor = MPT_ASYNC; + maxoffset = 0; + } else if (maxfactor < ioc->spi_data.minSyncFactor) { + maxfactor = ioc->spi_data.minSyncFactor; + } + } else + maxfactor = MPT_ASYNC; + } + + /* Set the negotiation flags. + */ + negoFlags = ioc->spi_data.noQas; + if (!maxwidth) + negoFlags |= MPT_TARGET_NO_NEGO_WIDE; + + if (!maxoffset) + negoFlags |= MPT_TARGET_NO_NEGO_SYNC; + + if (flags & MPT_SCSICFG_USE_NVRAM) { + width = maxwidth; + factor = maxfactor; + offset = maxoffset; + } else { + width = 0; + factor = MPT_ASYNC; + offset = 0; + //negoFlags = 0; + //negoFlags = MPT_TARGET_NO_NEGO_SYNC; + } + +#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION + /* Force to async and narrow if DV has not been executed + * for this ID + */ + if ((hd->ioc->spi_data.dvStatus[id] & MPT_SCSICFG_DV_NOT_DONE) != 0) { + width = 0; + factor = MPT_ASYNC; + offset = 0; + } +#endif + + /* If id is not a raid volume, get the updated + * transmission settings from the target structure. + */ + if (hd->Targets && (pTarget = hd->Targets[id]) && !pTarget->raidVolume) { + width = pTarget->maxWidth; + factor = pTarget->minSyncFactor; + offset = pTarget->maxOffset; + negoFlags = pTarget->negoFlags; + pTarget = NULL; + } + + if (flags & MPT_SCSICFG_BLK_NEGO) + negoFlags = MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC; + + mptscsih_setDevicePage1Flags(width, factor, offset, + &requested, &configuration, negoFlags); + + /* Get a MF for this command. + */ + if ((mf = mpt_get_msg_frame(ScsiDoneCtx, ioc->id)) == NULL) { + dprintk((MYIOC_s_WARN_FMT "write SDP1: no msg frames!\n", + ioc->name)); + return -EAGAIN; + } + + ddvprintk((MYIOC_s_INFO_FMT "WriteSDP1 (mf=%p, id=%d, req=0x%x, cfg=0x%x)\n", + hd->ioc->name, mf, id, requested, configuration)); + + + /* Set the request and the data pointers. + * Request takes: 36 bytes (32 bit SGE) + * SCSI Device Page 1 requires 16 bytes + * 40 + 16 <= size of SCSI IO Request = 56 bytes + * and MF size >= 64 bytes. + * Place data at end of MF. + */ + pReq = (Config_t *)mf; + + req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); + frameOffset = ioc->req_sz - sizeof(SCSIDevicePage1_t); + + pData = (SCSIDevicePage1_t *)((u8 *) mf + frameOffset); + dataDma = ioc->req_frames_dma + (req_idx * ioc->req_sz) + frameOffset; + + /* Complete the request frame (same for all requests). + */ + pReq->Action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; + pReq->Reserved = 0; + pReq->ChainOffset = 0; + pReq->Function = MPI_FUNCTION_CONFIG; + pReq->Reserved1[0] = 0; + pReq->Reserved1[1] = 0; + pReq->Reserved1[2] = 0; + pReq->MsgFlags = 0; + for (ii=0; ii < 8; ii++) { + pReq->Reserved2[ii] = 0; + } + pReq->Header.PageVersion = ioc->spi_data.sdp1version; + pReq->Header.PageLength = ioc->spi_data.sdp1length; + pReq->Header.PageNumber = 1; + pReq->Header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE; + pReq->PageAddress = cpu_to_le32(id | (bus << 8 )); + + /* Add a SGE to the config request. + */ + flagsLength = MPT_SGE_FLAGS_SSIMPLE_WRITE | ioc->spi_data.sdp1length * 4; + + mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, dataDma); + + /* Set up the common data portion + */ + pData->Header.PageVersion = pReq->Header.PageVersion; + pData->Header.PageLength = pReq->Header.PageLength; + pData->Header.PageNumber = pReq->Header.PageNumber; + pData->Header.PageType = pReq->Header.PageType; + pData->RequestedParameters = cpu_to_le32(requested); + pData->Reserved = 0; + pData->Configuration = cpu_to_le32(configuration); + + dprintk((MYIOC_s_INFO_FMT + "write SDP1: id %d pgaddr 0x%x req 0x%x config 0x%x\n", + ioc->name, id, (id | (bus<<8)), + requested, configuration)); + + mptscsih_put_msgframe(ScsiDoneCtx, ioc->id, mf); + } + + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* mptscsih_taskmgmt_timeout - Call back for timeout on a + * task management request. + * @data: Pointer to MPT_SCSI_HOST recast as an unsigned long + * + */ +static void mptscsih_taskmgmt_timeout(unsigned long data) +{ + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *) data; + + dtmprintk((KERN_WARNING MYNAM ": %s: mptscsih_taskmgmt_timeout: " + "TM request timed out!\n", hd->ioc->name)); + + /* Delete the timer that triggered this callback. + * Remark: del_timer checks to make sure timer is active + * before deleting. + */ + del_timer(&hd->TMtimer); + + /* Call the reset handler. Already had a TM request + * timeout - so issue a diagnostic reset + */ + if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) { + printk((KERN_WARNING " Firmware Reload FAILED!!\n")); + } +#ifdef MPT_SCSI_USE_NEW_EH + else { + /* Because we have reset the IOC, no TM requests can be + * pending. So let's make sure the tmPending flag is reset. + */ + nehprintk((KERN_WARNING MYNAM + ": %s: mptscsih_taskmgmt_timeout\n", + hd->ioc->name)); + hd->tmPending = 0; + } +#endif + + return; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Bus Scan and Domain Validation functionality ... + */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * mptscsih_scandv_complete - Scan and DV callback routine registered + * to Fustion MPT (base) driver. + * + * @ioc: Pointer to MPT_ADAPTER structure + * @mf: Pointer to original MPT request frame + * @mr: Pointer to MPT reply frame (NULL if TurboReply) + * + * This routine is called from mpt.c::mpt_interrupt() at the completion + * of any SCSI IO request. + * This routine is registered with the Fusion MPT (base) driver at driver + * load/init time via the mpt_register() API call. + * + * Returns 1 indicating alloc'd request frame ptr should be freed. + * + * Remark: Sets a completion code and (possibly) saves sense data + * in the IOC member localReply structure. + * Used ONLY for DV and other internal commands. + */ +static int +mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) +{ + MPT_SCSI_HOST *hd; + SCSIIORequest_t *pReq; + int completionCode; + u16 req_idx; + + if ((mf == NULL) || + (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) { + printk(MYIOC_s_ERR_FMT + "ScanDvComplete, %s req frame ptr! (=%p)\n", + ioc->name, mf?"BAD":"NULL", (void *) mf); + goto wakeup; + } + + hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; + del_timer(&hd->timer); + req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); + hd->ScsiLookup[req_idx] = NULL; + pReq = (SCSIIORequest_t *) mf; + + if (mf != hd->cmdPtr) { + printk(MYIOC_s_WARN_FMT "ScanDvComplete (mf=%p, cmdPtr=%p, idx=%d)\n", + hd->ioc->name, (void *)mf, (void *) hd->cmdPtr, req_idx); + } + hd->cmdPtr = NULL; + + ddvprintk((MYIOC_s_INFO_FMT "ScanDvComplete (mf=%p,mr=%p,idx=%d)\n", + hd->ioc->name, mf, mr, req_idx)); + + atomic_dec(&queue_depth); + + hd->pLocal = &hd->localReply; + hd->pLocal->scsiStatus = 0; + + /* If target struct exists, clear sense valid flag. + */ + if (mr == NULL) { + completionCode = MPT_SCANDV_GOOD; + } else { + SCSIIOReply_t *pReply; + u16 status; + + pReply = (SCSIIOReply_t *) mr; + + status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK; + + ddvtprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh, SCSIStatus=%02xh, IOCLogInfo=%08xh\n", + status, pReply->SCSIState, pReply->SCSIStatus, + le32_to_cpu(pReply->IOCLogInfo))); + + switch(status) { + + case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */ + completionCode = MPT_SCANDV_SELECTION_TIMEOUT; + break; + + case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */ + case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */ + case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */ + case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */ + completionCode = MPT_SCANDV_DID_RESET; + break; + + case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */ + case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */ + case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */ + if (pReply->Function == MPI_FUNCTION_CONFIG) { + ConfigReply_t *pr = (ConfigReply_t *)mr; + completionCode = MPT_SCANDV_GOOD; + hd->pLocal->header.PageVersion = pr->Header.PageVersion; + hd->pLocal->header.PageLength = pr->Header.PageLength; + hd->pLocal->header.PageNumber = pr->Header.PageNumber; + hd->pLocal->header.PageType = pr->Header.PageType; + + } else if (pReply->Function == MPI_FUNCTION_RAID_ACTION) { + /* If the RAID Volume request is successful, + * return GOOD, else indicate that + * some type of error occurred. + */ + MpiRaidActionReply_t *pr = (MpiRaidActionReply_t *)mr; + if (pr->ActionStatus == MPI_RAID_ACTION_ASTATUS_SUCCESS) + completionCode = MPT_SCANDV_GOOD; + else + completionCode = MPT_SCANDV_SOME_ERROR; + + } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) { + u8 *sense_data; + int sz; + + /* save sense data in global structure + */ + completionCode = MPT_SCANDV_SENSE; + hd->pLocal->scsiStatus = pReply->SCSIStatus; + sense_data = ((u8 *)hd->ioc->sense_buf_pool + + (req_idx * MPT_SENSE_BUFFER_ALLOC)); + + sz = MIN (pReq->SenseBufferLength, + SCSI_STD_SENSE_BYTES); + memcpy(hd->pLocal->sense, sense_data, sz); + + ddvprintk((KERN_NOTICE " Check Condition, sense ptr %p\n", + sense_data)); + } else if (pReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_FAILED) { + if (pReq->CDB[0] == CMD_Inquiry) + completionCode = MPT_SCANDV_ISSUE_SENSE; + else + completionCode = MPT_SCANDV_DID_RESET; + } + else if (pReply->SCSIState & MPI_SCSI_STATE_NO_SCSI_STATUS) + completionCode = MPT_SCANDV_DID_RESET; + else if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED) + completionCode = MPT_SCANDV_DID_RESET; + else { + /* If no error, this will be equivalent + * to MPT_SCANDV_GOOD + */ + completionCode = MPT_SCANDV_GOOD; + hd->pLocal->scsiStatus = pReply->SCSIStatus; + } + break; + + case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */ + if (pReply->SCSIState & MPI_SCSI_STATE_TERMINATED) + completionCode = MPT_SCANDV_DID_RESET; + else + completionCode = MPT_SCANDV_SOME_ERROR; + break; + + default: + completionCode = MPT_SCANDV_SOME_ERROR; + break; + + } /* switch(status) */ + + ddvtprintk((KERN_NOTICE " completionCode set to %08xh\n", + completionCode)); + } /* end of address reply case */ + + hd->pLocal->completion = completionCode; + + /* MF and RF are freed in mpt_interrupt + */ +wakeup: + /* Free Chain buffers (will never chain) in scan or dv */ + //mptscsih_freeChainBuffers(hd, req_idx); + + /* + * Wake up the original calling thread + */ + scandv_wait_done = 1; +/* SAE: No wait queues */ +#ifdef XENO_KILLED + wake_up(&scandv_waitq); +#else + mdelay(100); +#endif + + return 1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* mptscsih_timer_expired - Call back for timer process. + * Used only for dv functionality. + * @data: Pointer to MPT_SCSI_HOST recast as an unsigned long + * + */ +static void mptscsih_timer_expired(unsigned long data) +{ + MPT_SCSI_HOST *hd = (MPT_SCSI_HOST *) data; +#ifndef MPT_SCSI_USE_NEW_EH + unsigned long flags; +#endif + + + ddvprintk((MYIOC_s_WARN_FMT "Timer Expired! Cmd %p\n", hd->ioc->name, hd->cmdPtr)); + + if (hd->cmdPtr) { + MPIHeader_t *cmd = (MPIHeader_t *)hd->cmdPtr; + + if (cmd->Function == MPI_FUNCTION_SCSI_IO_REQUEST) { + /* Desire to issue a task management request here. + * TM requests MUST be single threaded. + * If old eh code and no TM current, issue request. + * If new eh code, do nothing. Wait for OS cmd timeout + * for bus reset. + */ +#ifndef MPT_SCSI_USE_NEW_EH + SCSIIORequest_t *pReq = (SCSIIORequest_t *) hd->cmdPtr; + + spin_lock_irqsave(&hd->ioc->FreeQlock, flags); + if (hd->tmPending) { + spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); + return; + } else + hd->tmPending = 1; + spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags); + if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS, + pReq->Bus, 0, 0, 0, NO_SLEEP) < 0) { + printk(MYIOC_s_WARN_FMT "TM FAILED!\n", hd->ioc->name); + } +#else + ddvtprintk((MYIOC_s_NOTE_FMT "DV Cmd Timeout: NoOp\n", hd->ioc->name)); +#endif + } else { + /* Perform a FW reload */ + if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) { + printk(MYIOC_s_WARN_FMT "Firmware Reload FAILED!\n", hd->ioc->name); + } + } + } else { + /* This should NEVER happen */ + printk(MYIOC_s_WARN_FMT "Null cmdPtr!!!!\n", hd->ioc->name); + } + + /* No more processing. + * TM call will generate an interrupt for SCSI TM Management. + * The FW will reply to all outstanding commands, callback will finish cleanup. + * Hard reset clean-up will free all resources. + */ + ddvprintk((MYIOC_s_WARN_FMT "Timer Expired Complete!\n", hd->ioc->name)); + + return; +} + +#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* mptscsih_do_raid - Format and Issue a RAID volume request message. + * @hd: Pointer to scsi host structure + * @action: What do be done. + * @id: Logical target id. + * @bus: Target locations bus. + * + * Returns: < 0 on a fatal error + * 0 on success + * + * Remark: Wait to return until reply processed by the ISR. + */ +static int +mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io) +{ + MpiRaidActionRequest_t *pReq; + MPT_FRAME_HDR *mf; + int in_isr; + + in_isr = in_interrupt(); + if (in_isr) { + dprintk((MYIOC_s_WARN_FMT "Internal raid request not allowed in ISR context!\n", + hd->ioc->name)); + return -EPERM; + } + + /* Get and Populate a free Frame + */ + if ((mf = mpt_get_msg_frame(ScsiScanDvCtx, hd->ioc->id)) == NULL) { + ddvprintk((MYIOC_s_WARN_FMT "_do_raid: no msg frames!\n", + hd->ioc->name)); + return -EAGAIN; + } + pReq = (MpiRaidActionRequest_t *)mf; + pReq->Action = action; + pReq->Reserved1 = 0; + pReq->ChainOffset = 0; + pReq->Function = MPI_FUNCTION_RAID_ACTION; + pReq->VolumeID = io->id; + pReq->VolumeBus = io->bus; + pReq->PhysDiskNum = io->physDiskNum; + pReq->MsgFlags = 0; + pReq->Reserved2 = 0; + pReq->ActionDataWord = 0; /* Reserved for this action */ + //pReq->ActionDataSGE = 0; + + mpt_add_sge((char *)&pReq->ActionDataSGE, + MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1); + + ddvprintk((MYIOC_s_INFO_FMT "RAID Volume action %x id %d\n", + hd->ioc->name, action, io->id)); + + hd->pLocal = NULL; + hd->timer.expires = jiffies + HZ*2; /* 2 second timeout */ + scandv_wait_done = 0; + + /* Save cmd pointer, for resource free if timeout or + * FW reload occurs + */ + hd->cmdPtr = mf; + + add_timer(&hd->timer); + mptscsih_put_msgframe(ScsiScanDvCtx, hd->ioc->id, mf); +/* SAE: No wait queues */ +#ifdef XENO_KILLED + wait_event(scandv_waitq, scandv_wait_done); +#else + /* SAE: Decrease the wait time: 100 -> 1 */ + while(!scandv_wait_done) { + mdelay(1); + } +#endif + + if ((hd->pLocal == NULL) || (hd->pLocal->completion != MPT_SCANDV_GOOD)) + return -1; + + return 0; +} +#endif /* ~MPTSCSIH_DISABLE_DOMAIN_VALIDATION */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_do_cmd - Do internal command. + * @hd: MPT_SCSI_HOST pointer + * @io: INTERNAL_CMD pointer. + * + * Issue the specified internally generated command and do command + * specific cleanup. For bus scan / DV only. + * NOTES: If command is Inquiry and status is good, + * initialize a target structure, save the data + * + * Remark: Single threaded access only. + * + * Return: + * < 0 if an illegal command or no resources + * + * 0 if good + * + * > 0 if command complete but some type of completion error. + */ +static int +mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *io) +{ + MPT_FRAME_HDR *mf; + SCSIIORequest_t *pScsiReq; + SCSIIORequest_t ReqCopy; + int my_idx, ii, dir; + int rc, cmdTimeout; + int in_isr; + char cmdLen; + char CDB[]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + char cmd = io->cmd; + + in_isr = in_interrupt(); + + if (in_isr) { + dprintk((MYIOC_s_WARN_FMT "Internal SCSI IO request not allowed in ISR context!\n", + hd->ioc->name)); + return -EPERM; + } + + + /* Set command specific information + */ + switch (cmd) { + case CMD_Inquiry: + cmdLen = 6; + dir = MPI_SCSIIO_CONTROL_READ; + CDB[0] = cmd; + CDB[4] = io->size; + cmdTimeout = 10; + break; + + case CMD_TestUnitReady: + cmdLen = 6; + dir = MPI_SCSIIO_CONTROL_READ; + cmdTimeout = 10; + break; + + case CMD_StartStopUnit: + cmdLen = 6; + dir = MPI_SCSIIO_CONTROL_READ; + CDB[0] = cmd; + CDB[4] = 1; /*Spin up the disk */ + cmdTimeout = 15; + break; + + case CMD_RequestSense: + cmdLen = 6; + CDB[0] = cmd; + CDB[4] = io->size; + dir = MPI_SCSIIO_CONTROL_READ; + cmdTimeout = 10; + break; + + case CMD_ReadBuffer: + cmdLen = 10; + dir = MPI_SCSIIO_CONTROL_READ; + CDB[0] = cmd; + if (io->flags & MPT_ICFLAG_ECHO) { + CDB[1] = 0x0A; + } else { + CDB[1] = 0x02; + } + + if (io->flags & MPT_ICFLAG_BUF_CAP) { + CDB[1] |= 0x01; + } + CDB[6] = (io->size >> 16) & 0xFF; + CDB[7] = (io->size >> 8) & 0xFF; + CDB[8] = io->size & 0xFF; + cmdTimeout = 10; + break; + + case CMD_WriteBuffer: + cmdLen = 10; + dir = MPI_SCSIIO_CONTROL_WRITE; + CDB[0] = cmd; + if (io->flags & MPT_ICFLAG_ECHO) { + CDB[1] = 0x0A; + } else { + CDB[1] = 0x02; + } + CDB[6] = (io->size >> 16) & 0xFF; + CDB[7] = (io->size >> 8) & 0xFF; + CDB[8] = io->size & 0xFF; + cmdTimeout = 10; + break; + + case CMD_Reserve6: + cmdLen = 6; + dir = MPI_SCSIIO_CONTROL_READ; + CDB[0] = cmd; + cmdTimeout = 10; + break; + + case CMD_Release6: + cmdLen = 6; + dir = MPI_SCSIIO_CONTROL_READ; + CDB[0] = cmd; + cmdTimeout = 10; + break; + + case CMD_SynchronizeCache: + cmdLen = 10; + dir = MPI_SCSIIO_CONTROL_READ; + CDB[0] = cmd; +// CDB[1] = 0x02; /* set immediate bit */ + cmdTimeout = 10; + break; + + default: + /* Error Case */ + return -EFAULT; + } + + /* Get and Populate a free Frame + */ + if ((mf = mpt_get_msg_frame(ScsiScanDvCtx, hd->ioc->id)) == NULL) { + ddvprintk((MYIOC_s_WARN_FMT "No msg frames!\n", + hd->ioc->name)); + return -EBUSY; + } + + pScsiReq = (SCSIIORequest_t *) mf; + + /* Get the request index */ + my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); + ADD_INDEX_LOG(my_idx); /* for debug */ + + if (io->flags & MPT_ICFLAG_PHYS_DISK) { + pScsiReq->TargetID = io->physDiskNum; + pScsiReq->Bus = 0; + pScsiReq->ChainOffset = 0; + pScsiReq->Function = MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH; + } else { + pScsiReq->TargetID = io->id; + pScsiReq->Bus = io->bus; + pScsiReq->ChainOffset = 0; + pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST; + } + + pScsiReq->CDBLength = cmdLen; + pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE; + + pScsiReq->Reserved = 0; + + pScsiReq->MsgFlags = mpt_msg_flags(); + /* MsgContext set in mpt_get_msg_fram call */ + + for (ii=0; ii < 8; ii++) + pScsiReq->LUN[ii] = 0; + pScsiReq->LUN[1] = io->lun; + + if (io->flags & MPT_ICFLAG_TAGGED_CMD) + pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_SIMPLEQ); + else + pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED); + + if (cmd == CMD_RequestSense) { + pScsiReq->Control = cpu_to_le32(dir | MPI_SCSIIO_CONTROL_UNTAGGED); + ddvprintk((MYIOC_s_INFO_FMT "Untagged! 0x%2x\n", + hd->ioc->name, cmd)); + } + + for (ii=0; ii < 16; ii++) + pScsiReq->CDB[ii] = CDB[ii]; + + pScsiReq->DataLength = cpu_to_le32(io->size); + pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma + + (my_idx * MPT_SENSE_BUFFER_ALLOC)); + + ddvprintk((MYIOC_s_INFO_FMT "Sending Command 0x%x for (%d:%d:%d)\n", + hd->ioc->name, cmd, io->bus, io->id, io->lun)); + + if (dir == MPI_SCSIIO_CONTROL_READ) { + mpt_add_sge((char *) &pScsiReq->SGL, + MPT_SGE_FLAGS_SSIMPLE_READ | io->size, + io->data_dma); + } else { + mpt_add_sge((char *) &pScsiReq->SGL, + MPT_SGE_FLAGS_SSIMPLE_WRITE | io->size, + io->data_dma); + } + + /* The ISR will free the request frame, but we need + * the information to initialize the target. Duplicate. + */ + memcpy(&ReqCopy, pScsiReq, sizeof(SCSIIORequest_t)); + + /* Issue this command after: + * finish init + * add timer + * Wait until the reply has been received + * ScsiScanDvCtx callback function will + * set hd->pLocal; + * set scandv_wait_done and call wake_up + */ + hd->pLocal = NULL; + hd->timer.expires = jiffies + HZ*cmdTimeout; + scandv_wait_done = 0; + + /* Save cmd pointer, for resource free if timeout or + * FW reload occurs + */ + hd->cmdPtr = mf; + + add_timer(&hd->timer); + mptscsih_put_msgframe(ScsiScanDvCtx, hd->ioc->id, mf); +/* SAE: No wait queues */ +#ifdef XENO_KILLED + wait_event(scandv_waitq, scandv_wait_done); +#else + /* SAE: Decrease the wait time: 100 -> 1 */ + while(!scandv_wait_done) { + mdelay(1); + } +#endif + + if (hd->pLocal) { + rc = hd->pLocal->completion; + hd->pLocal->skip = 0; + + /* Always set fatal error codes in some cases. + */ + if (rc == MPT_SCANDV_SELECTION_TIMEOUT) + rc = -ENXIO; + else if (rc == MPT_SCANDV_SOME_ERROR) + rc = -rc; + } else { + rc = -EFAULT; + /* This should never happen. */ + ddvprintk((MYIOC_s_INFO_FMT "_do_cmd: Null pLocal!!!\n", + hd->ioc->name)); + } + + return rc; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_synchronize_cache - Send SYNCHRONIZE_CACHE to all disks. + * @hd: Pointer to MPT_SCSI_HOST structure + * @portnum: IOC port number + * + * Uses the ISR, but with special processing. + * MUST be single-threaded. + * + * Return: 0 on completion + */ +static int +mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, int portnum) +{ + MPT_ADAPTER *ioc= hd->ioc; + VirtDevice *pTarget = NULL; + SCSIDevicePage1_t *pcfg1Data = NULL; + INTERNAL_CMD iocmd; + CONFIGPARMS cfg; + dma_addr_t cfg1_dma_addr = -1; + ConfigPageHeader_t header1; + int bus = 0; + int id = 0; + int lun = 0; + int hostId = ioc->pfacts[portnum].PortSCSIID; + int max_id; + int requested, configuration, data; + int doConfig = 0; + u8 flags, factor; + + max_id = ioc->sh->max_id - 1; + + /* Following parameters will not change + * in this routine. + */ + iocmd.cmd = CMD_SynchronizeCache; + iocmd.flags = 0; + iocmd.physDiskNum = -1; + iocmd.data = NULL; + iocmd.data_dma = -1; + iocmd.size = 0; + iocmd.rsvd = iocmd.rsvd2 = 0; + + /* No SCSI hosts + */ + if (hd->Targets == NULL) + return 0; + + /* Skip the host + */ + if (id == hostId) + id++; + + /* Write SDP1 for all SCSI devices + * Alloc memory and set up config buffer + */ + if (hd->is_spi) { + if (ioc->spi_data.sdp1length > 0) { + pcfg1Data = (SCSIDevicePage1_t *)pci_alloc_consistent(ioc->pcidev, + ioc->spi_data.sdp1length * 4, &cfg1_dma_addr); + + if (pcfg1Data != NULL) { + doConfig = 1; + header1.PageVersion = ioc->spi_data.sdp1version; + header1.PageLength = ioc->spi_data.sdp1length; + header1.PageNumber = 1; + header1.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE; + cfg.hdr = &header1; + cfg.physAddr = cfg1_dma_addr; + cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; + cfg.dir = 1; + cfg.timeout = 0; + } + } + } + + /* loop through all devices on this port + */ + while (bus < MPT_MAX_BUS) { + iocmd.bus = bus; + iocmd.id = id; + pTarget = hd->Targets[(int)id]; + + if (doConfig) { + + /* Set the negotiation flags */ + if (pTarget && (pTarget = hd->Targets[id]) && !pTarget->raidVolume) { + flags = pTarget->negoFlags; + } else { + flags = hd->ioc->spi_data.noQas; + if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) { + data = hd->ioc->spi_data.nvram[id]; + + if (data & MPT_NVRAM_WIDE_DISABLE) + flags |= MPT_TARGET_NO_NEGO_WIDE; + + factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT; + if ((factor == 0) || (factor == MPT_ASYNC)) + flags |= MPT_TARGET_NO_NEGO_SYNC; + } + } + + /* Force to async, narrow */ + mptscsih_setDevicePage1Flags(0, MPT_ASYNC, 0, &requested, + &configuration, flags); + pcfg1Data->RequestedParameters = le32_to_cpu(requested); + pcfg1Data->Reserved = 0; + pcfg1Data->Configuration = le32_to_cpu(configuration); + cfg.pageAddr = (bus<<8) | id; + mpt_config(hd->ioc, &cfg); + } + + /* If target Ptr NULL or if this target is NOT a disk, skip. + */ + // if (pTarget && ((pTarget->inq_data[0] & 0x1F) == 0)) { + if ((pTarget) && (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)){ + for (lun=0; lun <= MPT_LAST_LUN; lun++) { + /* If LUN present, issue the command + */ + if (pTarget->luns & (1< max_id) { + id = 0; + bus++; + } + } + + if (pcfg1Data) { + pci_free_consistent(ioc->pcidev, header1.PageLength * 4, pcfg1Data, cfg1_dma_addr); + } + + return 0; +} + +#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_domainValidation - Top level handler for domain validation. + * @hd: Pointer to MPT_SCSI_HOST structure. + * + * Uses the ISR, but with special processing. + * Called from schedule, should not be in interrupt mode. + * While thread alive, do dv for all devices needing dv + * + * Return: None. + */ +static void +mptscsih_domainValidation(void *arg) +{ + MPT_SCSI_HOST *hd = NULL; + MPT_ADAPTER *ioc = NULL; + unsigned long flags; + int id, maxid, dvStatus, did; + int ii, isPhysDisk; + + spin_lock_irqsave(&dvtaskQ_lock, flags); + dvtaskQ_active = 1; + if (dvtaskQ_release) { + dvtaskQ_active = 0; + spin_unlock_irqrestore(&dvtaskQ_lock, flags); + return; + } + spin_unlock_irqrestore(&dvtaskQ_lock, flags); + + /* For this ioc, loop through all devices and do dv to each device. + * When complete with this ioc, search through the ioc list, and + * for each scsi ioc found, do dv for all devices. Exit when no + * device needs dv. + */ + did = 1; + while (did) { + did = 0; + for (ioc = mpt_adapter_find_first(); ioc != NULL; ioc = mpt_adapter_find_next(ioc)) { + spin_lock_irqsave(&dvtaskQ_lock, flags); + if (dvtaskQ_release) { + dvtaskQ_active = 0; + spin_unlock_irqrestore(&dvtaskQ_lock, flags); + return; + } + spin_unlock_irqrestore(&dvtaskQ_lock, flags); + +/* SAE: */ +#ifdef XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/4); +#else + /* SAE: Decrease the wait time: 250 -> 100 */ + mdelay(100); +#endif + + /* DV only to SCSI adapters */ + if ((int)ioc->chip_type <= (int)FC929) + continue; + + /* Make sure everything looks ok */ + if (ioc->sh == NULL) + continue; + + hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; + if (hd == NULL) + continue; + + if ((ioc->spi_data.forceDv & MPT_SCSICFG_RELOAD_IOC_PG3) != 0) { + mpt_read_ioc_pg_3(ioc); + if (ioc->spi_data.pIocPg3) { + Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk; + int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks; + + while (numPDisk) { + if (ioc->spi_data.dvStatus[pPDisk->PhysDiskID] & MPT_SCSICFG_DV_NOT_DONE) + ioc->spi_data.dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV; + + pPDisk++; + numPDisk--; + } + } + ioc->spi_data.forceDv &= ~MPT_SCSICFG_RELOAD_IOC_PG3; + } + + maxid = MIN (ioc->sh->max_id, MPT_MAX_SCSI_DEVICES); + + for (id = 0; id < maxid; id++) { + spin_lock_irqsave(&dvtaskQ_lock, flags); + if (dvtaskQ_release) { + dvtaskQ_active = 0; + spin_unlock_irqrestore(&dvtaskQ_lock, flags); + return; + } + spin_unlock_irqrestore(&dvtaskQ_lock, flags); + dvStatus = hd->ioc->spi_data.dvStatus[id]; + + if (dvStatus & MPT_SCSICFG_NEED_DV) { + did++; + hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_DV_PENDING; + hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_NEED_DV; + +/* SAE: */ +#ifdef XENO_KILLED + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/4); +#else + /* SAE: Decrease the wait time: 250 -> 100 */ + mdelay(100); +#endif + + /* If hidden phys disk, block IO's to all + * raid volumes + * else, process normally + */ + isPhysDisk = mptscsih_is_phys_disk(ioc, id); + if (isPhysDisk) { + for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) { + if (hd->ioc->spi_data.isRaid & (1 << ii)) { + hd->ioc->spi_data.dvStatus[ii] |= MPT_SCSICFG_DV_PENDING; + } + } + } + + if (mptscsih_doDv(hd, 0, id) == 1) { + /* Untagged device was busy, try again + */ + hd->ioc->spi_data.dvStatus[id] |= MPT_SCSICFG_NEED_DV; + hd->ioc->spi_data.dvStatus[id] &= ~MPT_SCSICFG_DV_PENDING; + } else { + /* DV is complete. Clear flags. + */ + hd->ioc->spi_data.dvStatus[id] &= ~(MPT_SCSICFG_DV_NOT_DONE | MPT_SCSICFG_DV_PENDING); + } + + if (isPhysDisk) { + for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) { + if (hd->ioc->spi_data.isRaid & (1 << ii)) { + hd->ioc->spi_data.dvStatus[ii] &= ~MPT_SCSICFG_DV_PENDING; + } + } + } + + /* Post OS IOs that were pended while + * DV running. + */ + post_pendingQ_commands(hd); + + if (hd->ioc->spi_data.noQas) + mptscsih_qas_check(hd, id); + } + } + } + } + + spin_lock_irqsave(&dvtaskQ_lock, flags); + dvtaskQ_active = 0; + spin_unlock_irqrestore(&dvtaskQ_lock, flags); + + return; +} + +/* Search IOC page 3 to determine if this is hidden physical disk + */ +static int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id) +{ + if (ioc->spi_data.pIocPg3) { + Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk; + int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks; + + while (numPDisk) { + if (pPDisk->PhysDiskID == id) { + return 1; + } + pPDisk++; + numPDisk--; + } + } + return 0; +} + +/* Write SDP1 if no QAS has been enabled + */ +static void mptscsih_qas_check(MPT_SCSI_HOST *hd, int id) +{ + VirtDevice *pTarget = NULL; + int ii; + + if (hd->Targets == NULL) + return; + + for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++) { + if (ii == id) + continue; + + if ((hd->ioc->spi_data.dvStatus[ii] & MPT_SCSICFG_DV_NOT_DONE) != 0) + continue; + + pTarget = hd->Targets[ii]; + + if ((pTarget != NULL) && (!pTarget->raidVolume)) { + if ((pTarget->negoFlags & hd->ioc->spi_data.noQas) == 0) { + pTarget->negoFlags |= hd->ioc->spi_data.noQas; + mptscsih_writeSDP1(hd, 0, ii, 0); + } + } else { + if (mptscsih_is_phys_disk(hd->ioc, ii) == 1) + mptscsih_writeSDP1(hd, 0, ii, MPT_SCSICFG_USE_NVRAM); + } + } + return; +} + + + +#define MPT_GET_NVRAM_VALS 0x01 +#define MPT_UPDATE_MAX 0x02 +#define MPT_SET_MAX 0x04 +#define MPT_SET_MIN 0x08 +#define MPT_FALLBACK 0x10 +#define MPT_SAVE 0x20 + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/** + * mptscsih_doDv - Perform domain validation to a target. + * @hd: Pointer to MPT_SCSI_HOST structure. + * @portnum: IOC port number. + * @target: Physical ID of this target + * + * Uses the ISR, but with special processing. + * MUST be single-threaded. + * Test will exit if target is at async & narrow. + * + * Return: None. + */ +static int +mptscsih_doDv(MPT_SCSI_HOST *hd, int bus_number, int id) +{ + MPT_ADAPTER *ioc = hd->ioc; + VirtDevice *pTarget = NULL; + SCSIDevicePage1_t *pcfg1Data = NULL; + SCSIDevicePage0_t *pcfg0Data = NULL; + u8 *pbuf1 = NULL; + u8 *pbuf2 = NULL; + u8 *pDvBuf = NULL; + dma_addr_t dvbuf_dma = -1; + dma_addr_t buf1_dma = -1; + dma_addr_t buf2_dma = -1; + dma_addr_t cfg1_dma_addr = -1; + dma_addr_t cfg0_dma_addr = -1; + ConfigPageHeader_t header1; + ConfigPageHeader_t header0; + DVPARAMETERS dv; + INTERNAL_CMD iocmd; + CONFIGPARMS cfg; + int dv_alloc = 0; + int rc, sz = 0; + int bufsize = 0; + int dataBufSize = 0; + int echoBufSize = 0; + int notDone; + int patt; + int repeat; + int retcode = 0; + int nfactor = MPT_ULTRA320; + char firstPass = 1; + char doFallback = 0; + char readPage0; + char bus, lun; + char inq0 = 0; + + if (ioc->spi_data.sdp1length == 0) + return 0; + + if (ioc->spi_data.sdp0length == 0) + return 0; + + /* If multiple buses are used, require that the initiator + * id be the same on all buses. + */ + if (id == ioc->pfacts[0].PortSCSIID) + return 0; + + lun = 0; + bus = (u8) bus_number; + ddvtprintk((MYIOC_s_NOTE_FMT + "DV started: numIOs %d bus=%d, id %d dv @ %p\n", + ioc->name, atomic_read(&queue_depth), bus, id, &dv)); + + /* Prep DV structure + */ + memset (&dv, 0, sizeof(DVPARAMETERS)); + dv.id = id; + + /* Populate tmax with the current maximum + * transfer parameters for this target. + * Exit if narrow and async. + */ + dv.cmd = MPT_GET_NVRAM_VALS; + mptscsih_dv_parms(hd, &dv, NULL); + if ((!dv.max.width) && (!dv.max.offset)) + return 0; + + /* Prep SCSI IO structure + */ + iocmd.id = id; + iocmd.bus = bus; + iocmd.lun = lun; + iocmd.flags = 0; + iocmd.physDiskNum = -1; + iocmd.rsvd = iocmd.rsvd2 = 0; + + pTarget = hd->Targets[id]; + if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) { + /* Another GEM workaround. Check peripheral device type, + * if PROCESSOR, quit DV. + */ + if (((pTarget->inq_data[0] & 0x1F) == 0x03) || ((pTarget->inq_data[0] & 0x1F) > 0x08)) { + pTarget->negoFlags |= (MPT_TARGET_NO_NEGO_WIDE | MPT_TARGET_NO_NEGO_SYNC); + return 0; + } + } + + /* Use tagged commands if possible. + */ + if (pTarget) { + if (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES) + iocmd.flags |= MPT_ICFLAG_TAGGED_CMD; + else { + if (hd->ioc->facts.FWVersion.Word < 0x01000600) + return 0; + + if ((hd->ioc->facts.FWVersion.Word >= 0x01010000) && + (hd->ioc->facts.FWVersion.Word < 0x01010B00)) + return 0; + } + } + + /* Prep cfg structure + */ + cfg.pageAddr = (bus<<8) | id; + cfg.hdr = NULL; + + /* Prep SDP0 header + */ + header0.PageVersion = ioc->spi_data.sdp0version; + header0.PageLength = ioc->spi_data.sdp0length; + header0.PageNumber = 0; + header0.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE; + + /* Prep SDP1 header + */ + header1.PageVersion = ioc->spi_data.sdp1version; + header1.PageLength = ioc->spi_data.sdp1length; + header1.PageNumber = 1; + header1.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE; + + if (header0.PageLength & 1) + dv_alloc = (header0.PageLength * 4) + 4; + + dv_alloc += (2048 + (header1.PageLength * 4)); + + pDvBuf = pci_alloc_consistent(ioc->pcidev, dv_alloc, &dvbuf_dma); + if (pDvBuf == NULL) + return 0; + + sz = 0; + pbuf1 = (u8 *)pDvBuf; + buf1_dma = dvbuf_dma; + sz +=1024; + + pbuf2 = (u8 *) (pDvBuf + sz); + buf2_dma = dvbuf_dma + sz; + sz +=1024; + + pcfg0Data = (SCSIDevicePage0_t *) (pDvBuf + sz); + cfg0_dma_addr = dvbuf_dma + sz; + sz += header0.PageLength * 4; + + /* 8-byte alignment + */ + if (header0.PageLength & 1) + sz += 4; + + pcfg1Data = (SCSIDevicePage1_t *) (pDvBuf + sz); + cfg1_dma_addr = dvbuf_dma + sz; + + /* Skip this ID? Set cfg.hdr to force config page write + */ + { + ScsiCfgData *pspi_data = &hd->ioc->spi_data; + if (pspi_data->nvram && (pspi_data->nvram[id] != MPT_HOST_NVRAM_INVALID)) { + /* Set the factor from nvram */ + nfactor = (pspi_data->nvram[id] & MPT_NVRAM_SYNC_MASK) >> 8; + if (nfactor < pspi_data->minSyncFactor ) + nfactor = pspi_data->minSyncFactor; + + if (!(pspi_data->nvram[id] & MPT_NVRAM_ID_SCAN_ENABLE)) { + + ddvprintk((MYIOC_s_NOTE_FMT "DV Skipped: bus, id, lun (%d, %d, %d)\n", + ioc->name, bus, id, lun)); + + dv.cmd = MPT_SET_MAX; + mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); + cfg.hdr = &header1; + + /* Save the final negotiated settings to + * SCSI device page 1. + */ + cfg.physAddr = cfg1_dma_addr; + cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; + cfg.dir = 1; + mpt_config(hd->ioc, &cfg); + goto target_done; + } + } + } + + /* Finish iocmd inititialization - hidden or visible disk? */ + if (ioc->spi_data.pIocPg3) { + /* Searc IOC page 3 for matching id + */ + Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk; + int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks; + + while (numPDisk) { + if (pPDisk->PhysDiskID == id) { + /* match */ + iocmd.flags |= MPT_ICFLAG_PHYS_DISK; + iocmd.physDiskNum = pPDisk->PhysDiskNum; + + /* Quiesce the IM + */ + if (mptscsih_do_raid(hd, MPI_RAID_ACTION_QUIESCE_PHYS_IO, &iocmd) < 0) { + ddvprintk((MYIOC_s_ERR_FMT "RAID Queisce FAILED!\n", ioc->name)); + goto target_done; + } + break; + } + pPDisk++; + numPDisk--; + } + } + + /* RAID Volume ID's may double for a physical device. If RAID but + * not a physical ID as well, skip DV. + */ + if ((hd->ioc->spi_data.isRaid & (1 << id)) && !(iocmd.flags & MPT_ICFLAG_PHYS_DISK)) + goto target_done; + + + /* Basic Test. + * Async & Narrow - Inquiry + * Async & Narrow - Inquiry + * Maximum transfer rate - Inquiry + * Compare buffers: + * If compare, test complete. + * If miscompare and first pass, repeat + * If miscompare and not first pass, fall back and repeat + */ + hd->pLocal = NULL; + readPage0 = 0; + sz = SCSI_STD_INQUIRY_BYTES; + rc = MPT_SCANDV_GOOD; + while (1) { + ddvprintk((MYIOC_s_NOTE_FMT "DV: Start Basic test.\n", ioc->name)); + retcode = 0; + dv.cmd = MPT_SET_MIN; + mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); + + cfg.hdr = &header1; + cfg.physAddr = cfg1_dma_addr; + cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; + cfg.dir = 1; + if (mpt_config(hd->ioc, &cfg) != 0) + goto target_done; + + /* Wide - narrow - wide workaround case + */ + if ((rc == MPT_SCANDV_ISSUE_SENSE) && dv.max.width) { + /* Send an untagged command to reset disk Qs corrupted + * when a parity error occurs on a Request Sense. + */ + if ((hd->ioc->facts.FWVersion.Word >= 0x01000600) || + ((hd->ioc->facts.FWVersion.Word >= 0x01010000) && + (hd->ioc->facts.FWVersion.Word < 0x01010B00)) ) { + + iocmd.cmd = CMD_RequestSense; + iocmd.data_dma = buf1_dma; + iocmd.data = pbuf1; + iocmd.size = 0x12; + if (mptscsih_do_cmd(hd, &iocmd) < 0) + goto target_done; + else { + if (hd->pLocal == NULL) + goto target_done; + rc = hd->pLocal->completion; + if ((rc == MPT_SCANDV_GOOD) || (rc == MPT_SCANDV_SENSE)) { + dv.max.width = 0; + doFallback = 0; + } else + goto target_done; + } + } else + goto target_done; + } + + iocmd.cmd = CMD_Inquiry; + iocmd.data_dma = buf1_dma; + iocmd.data = pbuf1; + iocmd.size = sz; + if (mptscsih_do_cmd(hd, &iocmd) < 0) + goto target_done; + else { + if (hd->pLocal == NULL) + goto target_done; + rc = hd->pLocal->completion; + if (rc == MPT_SCANDV_GOOD) { + if (hd->pLocal->scsiStatus == STS_BUSY) { + if ((iocmd.flags & MPT_ICFLAG_TAGGED_CMD) == 0) + retcode = 1; + else + retcode = 0; + + goto target_done; + } + } else if (rc == MPT_SCANDV_SENSE) { + ; + } else { + /* If first command doesn't complete + * with a good status or with a check condition, + * exit. + */ + goto target_done; + } + } + + /* Reset the size for disks + */ + inq0 = (*pbuf1) & 0x1F; + if ((inq0 == 0) && pTarget && !pTarget->raidVolume) { + sz = 0x40; + iocmd.size = sz; + } + + /* Another GEM workaround. Check peripheral device type, + * if PROCESSOR, quit DV. + */ + if (((pbuf1[0] & 0x1F) == 0x03) || ((pbuf1[0] & 0x1F) > 0x08)) + goto target_done; + + if (mptscsih_do_cmd(hd, &iocmd) < 0) + goto target_done; + + if (sz == 0x40) { + if ((pTarget->maxWidth == 1) && (pTarget->maxOffset) && (nfactor < 0x0A) + && (pTarget->minSyncFactor > 0x09)) { + if ((pbuf1[56] & 0x04) == 0) + ; + else if ((pbuf1[56] & 0x01) == 1) { + pTarget->minSyncFactor = nfactor > MPT_ULTRA320 ? nfactor : MPT_ULTRA320; + } else { + pTarget->minSyncFactor = nfactor > MPT_ULTRA160 ? nfactor : MPT_ULTRA160; + } + + dv.max.factor = pTarget->minSyncFactor; + + if ((pbuf1[56] & 0x02) == 0) { + pTarget->negoFlags |= MPT_TARGET_NO_NEGO_QAS; + hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS; + } + } + } + + if (doFallback) + dv.cmd = MPT_FALLBACK; + else + dv.cmd = MPT_SET_MAX; + + mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); + if (mpt_config(hd->ioc, &cfg) != 0) + goto target_done; + + if ((!dv.now.width) && (!dv.now.offset)) + goto target_done; + + iocmd.cmd = CMD_Inquiry; + iocmd.data_dma = buf2_dma; + iocmd.data = pbuf2; + iocmd.size = sz; + if (mptscsih_do_cmd(hd, &iocmd) < 0) + goto target_done; + else if (hd->pLocal == NULL) + goto target_done; + else { + /* Save the return code. + * If this is the first pass, + * read SCSI Device Page 0 + * and update the target max parameters. + */ + rc = hd->pLocal->completion; + doFallback = 0; + if (rc == MPT_SCANDV_GOOD) { + if (!readPage0) { + u32 sdp0_info; + u32 sdp0_nego; + + cfg.hdr = &header0; + cfg.physAddr = cfg0_dma_addr; + cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT; + cfg.dir = 0; + + if (mpt_config(hd->ioc, &cfg) != 0) + goto target_done; + + sdp0_info = le32_to_cpu(pcfg0Data->Information) & 0x0E; + sdp0_nego = (le32_to_cpu(pcfg0Data->NegotiatedParameters) & 0xFF00 ) >> 8; + + /* Quantum and Fujitsu workarounds. + * Quantum: PPR U320 -> PPR reply with Ultra2 and wide + * Fujitsu: PPR U320 -> Msg Reject and Ultra2 and wide + * Resetart with a request for U160. + */ + if ((dv.now.factor == MPT_ULTRA320) && (sdp0_nego == MPT_ULTRA2)) { + doFallback = 1; + } else { + dv.cmd = MPT_UPDATE_MAX; + mptscsih_dv_parms(hd, &dv, (void *)pcfg0Data); + /* Update the SCSI device page 1 area + */ + pcfg1Data->RequestedParameters = pcfg0Data->NegotiatedParameters; + readPage0 = 1; + } + } + + /* Quantum workaround. Restart this test will the fallback + * flag set. + */ + if (doFallback == 0) { + if (memcmp(pbuf1, pbuf2, sz) != 0) { + if (!firstPass) + doFallback = 1; + } else + break; /* test complete */ + } + + + } else if (rc == MPT_SCANDV_ISSUE_SENSE) + doFallback = 1; /* set fallback flag */ + else if ((rc == MPT_SCANDV_DID_RESET) || (rc == MPT_SCANDV_SENSE)) + doFallback = 1; /* set fallback flag */ + else + goto target_done; + + firstPass = 0; + } + } + ddvprintk((MYIOC_s_NOTE_FMT "DV: Basic test completed OK.\n", ioc->name)); + inq0 = (*pbuf1) & 0x1F; + + /* Continue only for disks + */ + if (inq0 != 0) + goto target_done; + + /* Start the Enhanced Test. + * 0) issue TUR to clear out check conditions + * 1) read capacity of echo (regular) buffer + * 2) reserve device + * 3) do write-read-compare data pattern test + * 4) release + * 5) update nego parms to target struct + */ + cfg.hdr = &header1; + cfg.physAddr = cfg1_dma_addr; + cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; + cfg.dir = 1; + + iocmd.cmd = CMD_TestUnitReady; + iocmd.data_dma = -1; + iocmd.data = NULL; + iocmd.size = 0; + notDone = 1; + while (notDone) { + if (mptscsih_do_cmd(hd, &iocmd) < 0) + goto target_done; + + if (hd->pLocal == NULL) + goto target_done; + + rc = hd->pLocal->completion; + if (rc == MPT_SCANDV_GOOD) + notDone = 0; + else if (rc == MPT_SCANDV_SENSE) { + u8 skey = hd->pLocal->sense[2] & 0x0F; + u8 asc = hd->pLocal->sense[12]; + u8 ascq = hd->pLocal->sense[13]; + ddvprintk((MYIOC_s_INFO_FMT + "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", + ioc->name, skey, asc, ascq)); + + if (skey == SK_UNIT_ATTENTION) + notDone++; /* repeat */ + else if ((skey == SK_NOT_READY) && + (asc == 0x04)&&(ascq == 0x01)) { + /* wait then repeat */ + mdelay (2000); + notDone++; + } else if ((skey == SK_NOT_READY) && (asc == 0x3A)) { + /* no medium, try read test anyway */ + notDone = 0; + } else { + /* All other errors are fatal. + */ + ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.", + ioc->name)); + goto target_done; + } + } else + goto target_done; + } + + iocmd.cmd = CMD_ReadBuffer; + iocmd.data_dma = buf1_dma; + iocmd.data = pbuf1; + iocmd.size = 4; + iocmd.flags |= MPT_ICFLAG_BUF_CAP; + + dataBufSize = 0; + echoBufSize = 0; + for (patt = 0; patt < 2; patt++) { + if (patt == 0) + iocmd.flags |= MPT_ICFLAG_ECHO; + else + iocmd.flags &= ~MPT_ICFLAG_ECHO; + + notDone = 1; + while (notDone) { + bufsize = 0; + + /* If not ready after 8 trials, + * give up on this device. + */ + if (notDone > 8) + goto target_done; + + if (mptscsih_do_cmd(hd, &iocmd) < 0) + goto target_done; + else if (hd->pLocal == NULL) + goto target_done; + else { + rc = hd->pLocal->completion; + ddvprintk(("ReadBuffer Comp Code %d", rc)); + ddvprintk((" buff: %0x %0x %0x %0x\n", + pbuf1[0], pbuf1[1], pbuf1[2], pbuf1[3])); + + if (rc == MPT_SCANDV_GOOD) { + notDone = 0; + if (iocmd.flags & MPT_ICFLAG_ECHO) { + bufsize = ((pbuf1[2] & 0x1F) <<8) | pbuf1[3]; + } else { + bufsize = pbuf1[1]<<16 | pbuf1[2]<<8 | pbuf1[3]; + } + } else if (rc == MPT_SCANDV_SENSE) { + u8 skey = hd->pLocal->sense[2] & 0x0F; + u8 asc = hd->pLocal->sense[12]; + u8 ascq = hd->pLocal->sense[13]; + ddvprintk((MYIOC_s_INFO_FMT + "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", + ioc->name, skey, asc, ascq)); + if (skey == SK_ILLEGAL_REQUEST) { + notDone = 0; + } else if (skey == SK_UNIT_ATTENTION) { + notDone++; /* repeat */ + } else if ((skey == SK_NOT_READY) && + (asc == 0x04)&&(ascq == 0x01)) { + /* wait then repeat */ + mdelay (2000); + notDone++; + } else { + /* All other errors are fatal. + */ + ddvprintk((MYIOC_s_INFO_FMT "DV: fatal error.", + ioc->name)); + goto target_done; + } + } else { + /* All other errors are fatal + */ + goto target_done; + } + } + } + + if (iocmd.flags & MPT_ICFLAG_ECHO) + echoBufSize = bufsize; + else + dataBufSize = bufsize; + } + sz = 0; + iocmd.flags &= ~MPT_ICFLAG_BUF_CAP; + + /* Use echo buffers if possible, + * Exit if both buffers are 0. + */ + if (echoBufSize > 0) { + iocmd.flags |= MPT_ICFLAG_ECHO; + if (dataBufSize > 0) + bufsize = MIN(echoBufSize, dataBufSize); + else + bufsize = echoBufSize; + } else if (dataBufSize == 0) + goto target_done; + + ddvprintk((MYIOC_s_INFO_FMT "%s Buffer Capacity %d\n", ioc->name, + (iocmd.flags & MPT_ICFLAG_ECHO) ? "Echo" : " ", bufsize)); + + /* Data buffers for write-read-compare test max 1K. + */ + sz = MIN(bufsize, 1024); + + /* --- loop ---- + * On first pass, always issue a reserve. + * On additional loops, only if a reset has occurred. + * iocmd.flags indicates if echo or regular buffer + */ + for (patt = 0; patt < 4; patt++) { + ddvprintk(("Pattern %d\n", patt)); + if ((iocmd.flags & MPT_ICFLAG_RESERVED) && (iocmd.flags & MPT_ICFLAG_DID_RESET)) { + iocmd.cmd = CMD_TestUnitReady; + iocmd.data_dma = -1; + iocmd.data = NULL; + iocmd.size = 0; + if (mptscsih_do_cmd(hd, &iocmd) < 0) + goto target_done; + + iocmd.cmd = CMD_Release6; + iocmd.data_dma = -1; + iocmd.data = NULL; + iocmd.size = 0; + if (mptscsih_do_cmd(hd, &iocmd) < 0) + goto target_done; + else if (hd->pLocal == NULL) + goto target_done; + else { + rc = hd->pLocal->completion; + ddvprintk(("Release rc %d\n", rc)); + if (rc == MPT_SCANDV_GOOD) + iocmd.flags &= ~MPT_ICFLAG_RESERVED; + else + goto target_done; + } + iocmd.flags &= ~MPT_ICFLAG_RESERVED; + } + iocmd.flags &= ~MPT_ICFLAG_DID_RESET; + + repeat = 5; + while (repeat && (!(iocmd.flags & MPT_ICFLAG_RESERVED))) { + iocmd.cmd = CMD_Reserve6; + iocmd.data_dma = -1; + iocmd.data = NULL; + iocmd.size = 0; + if (mptscsih_do_cmd(hd, &iocmd) < 0) + goto target_done; + else if (hd->pLocal == NULL) + goto target_done; + else { + rc = hd->pLocal->completion; + if (rc == MPT_SCANDV_GOOD) { + iocmd.flags |= MPT_ICFLAG_RESERVED; + } else if (rc == MPT_SCANDV_SENSE) { + /* Wait if coming ready + */ + u8 skey = hd->pLocal->sense[2] & 0x0F; + u8 asc = hd->pLocal->sense[12]; + u8 ascq = hd->pLocal->sense[13]; + ddvprintk((MYIOC_s_INFO_FMT + "DV: Reserve Failed: ", ioc->name)); + ddvprintk(("SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", + skey, asc, ascq)); + + if ((skey == SK_NOT_READY) && (asc == 0x04)&& + (ascq == 0x01)) { + /* wait then repeat */ + mdelay (2000); + notDone++; + } else { + ddvprintk((MYIOC_s_INFO_FMT + "DV: Reserved Failed.", ioc->name)); + goto target_done; + } + } else { + ddvprintk((MYIOC_s_INFO_FMT "DV: Reserved Failed.", + ioc->name)); + goto target_done; + } + } + } + + mptscsih_fillbuf(pbuf1, sz, patt, 1); + iocmd.cmd = CMD_WriteBuffer; + iocmd.data_dma = buf1_dma; + iocmd.data = pbuf1; + iocmd.size = sz; + if (mptscsih_do_cmd(hd, &iocmd) < 0) + goto target_done; + else if (hd->pLocal == NULL) + goto target_done; + else { + rc = hd->pLocal->completion; + if (rc == MPT_SCANDV_GOOD) + ; /* Issue read buffer */ + else if (rc == MPT_SCANDV_DID_RESET) { + /* If using echo buffers, reset to data buffers. + * Else do Fallback and restart + * this test (re-issue reserve + * because of bus reset). + */ + if ((iocmd.flags & MPT_ICFLAG_ECHO) && (dataBufSize >= bufsize)) { + iocmd.flags &= ~MPT_ICFLAG_ECHO; + } else { + dv.cmd = MPT_FALLBACK; + mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); + + if (mpt_config(hd->ioc, &cfg) != 0) + goto target_done; + + if ((!dv.now.width) && (!dv.now.offset)) + goto target_done; + } + + iocmd.flags |= MPT_ICFLAG_DID_RESET; + patt = -1; + continue; + } else if (rc == MPT_SCANDV_SENSE) { + /* Restart data test if UA, else quit. + */ + u8 skey = hd->pLocal->sense[2] & 0x0F; + ddvprintk((MYIOC_s_INFO_FMT + "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey, + hd->pLocal->sense[12], hd->pLocal->sense[13])); + if (skey == SK_UNIT_ATTENTION) { + patt = -1; + continue; + } else if (skey == SK_ILLEGAL_REQUEST) { + if (iocmd.flags & MPT_ICFLAG_ECHO) { + if (dataBufSize >= bufsize) { + iocmd.flags &= ~MPT_ICFLAG_ECHO; + patt = -1; + continue; + } + } + goto target_done; + } + else + goto target_done; + } else { + /* fatal error */ + goto target_done; + } + } + + iocmd.cmd = CMD_ReadBuffer; + iocmd.data_dma = buf2_dma; + iocmd.data = pbuf2; + iocmd.size = sz; + if (mptscsih_do_cmd(hd, &iocmd) < 0) + goto target_done; + else if (hd->pLocal == NULL) + goto target_done; + else { + rc = hd->pLocal->completion; + if (rc == MPT_SCANDV_GOOD) { + /* If buffers compare, + * go to next pattern, + * else, do a fallback and restart + * data transfer test. + */ + if (memcmp (pbuf1, pbuf2, sz) == 0) { + ; /* goto next pattern */ + } else { + /* Miscompare with Echo buffer, go to data buffer, + * if that buffer exists. + * Miscompare with Data buffer, check first 4 bytes, + * some devices return capacity. Exit in this case. + */ + if (iocmd.flags & MPT_ICFLAG_ECHO) { + if (dataBufSize >= bufsize) + iocmd.flags &= ~MPT_ICFLAG_ECHO; + else + goto target_done; + } else { + if (dataBufSize == (pbuf2[1]<<16 | pbuf2[2]<<8 | pbuf2[3])) { + /* Argh. Device returning wrong data. + * Quit DV for this device. + */ + goto target_done; + } + + /* Had an actual miscompare. Slow down.*/ + dv.cmd = MPT_FALLBACK; + mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); + + if (mpt_config(hd->ioc, &cfg) != 0) + goto target_done; + + if ((!dv.now.width) && (!dv.now.offset)) + goto target_done; + } + + patt = -1; + continue; + } + } else if (rc == MPT_SCANDV_DID_RESET) { + /* Do Fallback and restart + * this test (re-issue reserve + * because of bus reset). + */ + dv.cmd = MPT_FALLBACK; + mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); + + if (mpt_config(hd->ioc, &cfg) != 0) + goto target_done; + + if ((!dv.now.width) && (!dv.now.offset)) + goto target_done; + + iocmd.flags |= MPT_ICFLAG_DID_RESET; + patt = -1; + continue; + } else if (rc == MPT_SCANDV_SENSE) { + /* Restart data test if UA, else quit. + */ + u8 skey = hd->pLocal->sense[2] & 0x0F; + ddvprintk((MYIOC_s_INFO_FMT + "SenseKey:ASC:ASCQ = (%x:%02x:%02x)\n", ioc->name, skey, + hd->pLocal->sense[12], hd->pLocal->sense[13])); + if (skey == SK_UNIT_ATTENTION) { + patt = -1; + continue; + } + else + goto target_done; + } else { + /* fatal error */ + goto target_done; + } + } + + } /* --- end of patt loop ---- */ + +target_done: + if (iocmd.flags & MPT_ICFLAG_RESERVED) { + iocmd.cmd = CMD_Release6; + iocmd.data_dma = -1; + iocmd.data = NULL; + iocmd.size = 0; + if (mptscsih_do_cmd(hd, &iocmd) < 0) + printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d", + ioc->name, id); + else if (hd->pLocal) { + if (hd->pLocal->completion == MPT_SCANDV_GOOD) + iocmd.flags &= ~MPT_ICFLAG_RESERVED; + } else { + printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d", + ioc->name, id); + } + } + + + /* Set if cfg1_dma_addr contents is valid + */ + if ((cfg.hdr != NULL) && (retcode == 0)){ + /* If disk, not U320, disable QAS + */ + if ((inq0 == 0) && (dv.now.factor > MPT_ULTRA320)) + hd->ioc->spi_data.noQas = MPT_TARGET_NO_NEGO_QAS; + + dv.cmd = MPT_SAVE; + mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data); + + /* Double writes to SDP1 can cause problems, + * skip save of the final negotiated settings to + * SCSI device page 1. + * + cfg.hdr = &header1; + cfg.physAddr = cfg1_dma_addr; + cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT; + cfg.dir = 1; + mpt_config(hd->ioc, &cfg); + */ + } + + /* If this is a RAID Passthrough, enable internal IOs + */ + if (iocmd.flags & MPT_ICFLAG_PHYS_DISK) { + if (mptscsih_do_raid(hd, MPI_RAID_ACTION_ENABLE_PHYS_IO, &iocmd) < 0) + ddvprintk((MYIOC_s_ERR_FMT "RAID Enable FAILED!\n", ioc->name)); + } + + /* Done with the DV scan of the current target + */ + if (pDvBuf) + pci_free_consistent(ioc->pcidev, dv_alloc, pDvBuf, dvbuf_dma); + + ddvtprintk((MYIOC_s_INFO_FMT "DV Done. IOs outstanding = %d\n", + ioc->name, atomic_read(&queue_depth))); + + return retcode; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* mptscsih_dv_parms - perform a variety of operations on the + * parameters used for negotiation. + * @hd: Pointer to a SCSI host. + * @dv: Pointer to a structure that contains the maximum and current + * negotiated parameters. + */ +static void +mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage) +{ + VirtDevice *pTarget = NULL; + SCSIDevicePage0_t *pPage0 = NULL; + SCSIDevicePage1_t *pPage1 = NULL; + int val = 0, data, configuration; + u8 width = 0; + u8 offset = 0; + u8 factor = 0; + u8 negoFlags = 0; + u8 cmd = dv->cmd; + u8 id = dv->id; + + switch (cmd) { + case MPT_GET_NVRAM_VALS: + ddvprintk((MYIOC_s_NOTE_FMT "Getting NVRAM: ", + hd->ioc->name)); + /* Get the NVRAM values and save in tmax + * If not an LVD bus, the adapter minSyncFactor has been + * already throttled back. + */ + if ((hd->Targets)&&((pTarget = hd->Targets[(int)id]) != NULL) && !pTarget->raidVolume) { + width = pTarget->maxWidth; + offset = pTarget->maxOffset; + factor = pTarget->minSyncFactor; + negoFlags = pTarget->negoFlags; + } else { + if (hd->ioc->spi_data.nvram && (hd->ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID)) { + data = hd->ioc->spi_data.nvram[id]; + width = data & MPT_NVRAM_WIDE_DISABLE ? 0 : 1; + if ((offset = hd->ioc->spi_data.maxSyncOffset) == 0) + factor = MPT_ASYNC; + else { + factor = (data & MPT_NVRAM_SYNC_MASK) >> MPT_NVRAM_SYNC_SHIFT; + if ((factor == 0) || (factor == MPT_ASYNC)){ + factor = MPT_ASYNC; + offset = 0; + } + } + } else { + width = MPT_NARROW; + offset = 0; + factor = MPT_ASYNC; + } + + /* Set the negotiation flags */ + negoFlags = hd->ioc->spi_data.noQas; + if (!width) + negoFlags |= MPT_TARGET_NO_NEGO_WIDE; + + if (!offset) + negoFlags |= MPT_TARGET_NO_NEGO_SYNC; + } + + /* limit by adapter capabilities */ + width = MIN(width, hd->ioc->spi_data.maxBusWidth); + offset = MIN(offset, hd->ioc->spi_data.maxSyncOffset); + factor = MAX(factor, hd->ioc->spi_data.minSyncFactor); + + /* Check Consistency */ + if (offset && (factor < MPT_ULTRA2) && !width) + factor = MPT_ULTRA2; + + dv->max.width = width; + dv->max.offset = offset; + dv->max.factor = factor; + dv->max.flags = negoFlags; + ddvprintk((" width %d, factor %x, offset %x flags %x\n", + width, factor, offset, negoFlags)); + break; + + case MPT_UPDATE_MAX: + ddvprintk((MYIOC_s_NOTE_FMT + "Updating with SDP0 Data: ", hd->ioc->name)); + /* Update tmax values with those from Device Page 0.*/ + pPage0 = (SCSIDevicePage0_t *) pPage; + if (pPage0) { + val = cpu_to_le32(pPage0->NegotiatedParameters); + dv->max.width = val & MPI_SCSIDEVPAGE0_NP_WIDE ? 1 : 0; + dv->max.offset = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_OFFSET_MASK) >> 16; + dv->max.factor = (val&MPI_SCSIDEVPAGE0_NP_NEG_SYNC_PERIOD_MASK) >> 8; + } + + dv->now.width = dv->max.width; + dv->now.offset = dv->max.offset; + dv->now.factor = dv->max.factor; + ddvprintk(("width %d, factor %x, offset %x, flags %x\n", + dv->now.width, dv->now.factor, dv->now.offset, dv->now.flags)); + break; + + case MPT_SET_MAX: + ddvprintk((MYIOC_s_NOTE_FMT "Setting Max: ", + hd->ioc->name)); + /* Set current to the max values. Update the config page.*/ + dv->now.width = dv->max.width; + dv->now.offset = dv->max.offset; + dv->now.factor = dv->max.factor; + dv->now.flags = dv->max.flags; + + pPage1 = (SCSIDevicePage1_t *)pPage; + if (pPage1) { + mptscsih_setDevicePage1Flags (dv->now.width, dv->now.factor, + dv->now.offset, &val, &configuration, dv->now.flags); + pPage1->RequestedParameters = le32_to_cpu(val); + pPage1->Reserved = 0; + pPage1->Configuration = le32_to_cpu(configuration); + + } + + ddvprintk(("width %d, factor %x, offset %x request %x, config %x\n", + dv->now.width, dv->now.factor, dv->now.offset, val, configuration)); + break; + + case MPT_SET_MIN: + ddvprintk((MYIOC_s_NOTE_FMT "Setting Min: ", + hd->ioc->name)); + /* Set page to asynchronous and narrow + * Do not update now, breaks fallback routine. */ + width = MPT_NARROW; + offset = 0; + factor = MPT_ASYNC; + negoFlags = dv->max.flags; + + pPage1 = (SCSIDevicePage1_t *)pPage; + if (pPage1) { + mptscsih_setDevicePage1Flags (width, factor, + offset, &val, &configuration, negoFlags); + pPage1->RequestedParameters = le32_to_cpu(val); + pPage1->Reserved = 0; + pPage1->Configuration = le32_to_cpu(configuration); + } + ddvprintk(("width %d, factor %x, offset %x request %x config %x\n", + width, factor, offset, val, configuration)); + break; + + case MPT_FALLBACK: + ddvprintk((MYIOC_s_NOTE_FMT + "Fallback: Start: offset %d, factor %x, width %d \n", + hd->ioc->name, dv->now.offset, + dv->now.factor, dv->now.width)); + width = dv->now.width; + offset = dv->now.offset; + factor = dv->now.factor; + if ((offset) && (dv->max.width)) { + if (factor < MPT_ULTRA160) + factor = MPT_ULTRA160; + else if (factor < MPT_ULTRA2) { + factor = MPT_ULTRA2; + width = MPT_WIDE; + } else if ((factor == MPT_ULTRA2) && width) { + factor = MPT_ULTRA2; + width = MPT_NARROW; + } else if (factor < MPT_ULTRA) { + factor = MPT_ULTRA; + width = MPT_WIDE; + } else if ((factor == MPT_ULTRA) && width) { + factor = MPT_ULTRA; + width = MPT_NARROW; + } else if (factor < MPT_FAST) { + factor = MPT_FAST; + width = MPT_WIDE; + } else if ((factor == MPT_FAST) && width) { + factor = MPT_FAST; + width = MPT_NARROW; + } else if (factor < MPT_SCSI) { + factor = MPT_SCSI; + width = MPT_WIDE; + } else if ((factor == MPT_SCSI) && width) { + factor = MPT_SCSI; + width = MPT_NARROW; + } else { + factor = MPT_ASYNC; + offset = 0; + } + + } else if (offset) { + width = MPT_NARROW; + if (factor < MPT_ULTRA) + factor = MPT_ULTRA; + else if (factor < MPT_FAST) + factor = MPT_FAST; + else if (factor < MPT_SCSI) + factor = MPT_SCSI; + else { + factor = MPT_ASYNC; + offset = 0; + } + + } else { + width = MPT_NARROW; + factor = MPT_ASYNC; + } + dv->max.flags |= MPT_TARGET_NO_NEGO_QAS; + + dv->now.width = width; + dv->now.offset = offset; + dv->now.factor = factor; + dv->now.flags = dv->max.flags; + + pPage1 = (SCSIDevicePage1_t *)pPage; + if (pPage1) { + mptscsih_setDevicePage1Flags (width, factor, offset, &val, + &configuration, dv->now.flags); + + pPage1->RequestedParameters = le32_to_cpu(val); + pPage1->Reserved = 0; + pPage1->Configuration = le32_to_cpu(configuration); + } + + ddvprintk(("Finish: offset %d, factor %x, width %d, request %x config %x\n", + dv->now.offset, dv->now.factor, dv->now.width, val, configuration)); + break; + + case MPT_SAVE: + ddvprintk((MYIOC_s_NOTE_FMT + "Saving to Target structure: ", hd->ioc->name)); + ddvprintk(("offset %d, factor %x, width %d \n", + dv->now.offset, dv->now.factor, dv->now.width)); + + /* Save these values to target structures + * or overwrite nvram (phys disks only). + */ + + if ((hd->Targets)&&((pTarget = hd->Targets[(int)id]) != NULL) && !pTarget->raidVolume ) { + pTarget->maxWidth = dv->now.width; + pTarget->maxOffset = dv->now.offset; + pTarget->minSyncFactor = dv->now.factor; + pTarget->negoFlags = dv->now.flags; + } else { + /* Preserv all flags, use + * read-modify-write algorithm + */ + if (hd->ioc->spi_data.nvram) { + data = hd->ioc->spi_data.nvram[id]; + + if (dv->now.width) + data &= ~MPT_NVRAM_WIDE_DISABLE; + else + data |= MPT_NVRAM_WIDE_DISABLE; + + if (!dv->now.offset) + factor = MPT_ASYNC; + + data &= ~MPT_NVRAM_SYNC_MASK; + data |= (dv->now.factor << MPT_NVRAM_SYNC_SHIFT) & MPT_NVRAM_SYNC_MASK; + + hd->ioc->spi_data.nvram[id] = data; + } + } + break; + } +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* mptscsih_fillbuf - fill a buffer with a special data pattern + * cleanup. For bus scan only. + * + * @buffer: Pointer to data buffer to be filled. + * @size: Number of bytes to fill + * @index: Pattern index + * @width: bus width, 0 (8 bits) or 1 (16 bits) + */ +static void +mptscsih_fillbuf(char *buffer, int size, int index, int width) +{ + char *ptr = buffer; + int ii; + char byte; + short val; + + switch (index) { + case 0: + + if (width) { + /* Pattern: 0000 FFFF 0000 FFFF + */ + for (ii=0; ii < size; ii++, ptr++) { + if (ii & 0x02) + *ptr = 0xFF; + else + *ptr = 0x00; + } + } else { + /* Pattern: 00 FF 00 FF + */ + for (ii=0; ii < size; ii++, ptr++) { + if (ii & 0x01) + *ptr = 0xFF; + else + *ptr = 0x00; + } + } + break; + + case 1: + if (width) { + /* Pattern: 5555 AAAA 5555 AAAA 5555 + */ + for (ii=0; ii < size; ii++, ptr++) { + if (ii & 0x02) + *ptr = 0xAA; + else + *ptr = 0x55; + } + } else { + /* Pattern: 55 AA 55 AA 55 + */ + for (ii=0; ii < size; ii++, ptr++) { + if (ii & 0x01) + *ptr = 0xAA; + else + *ptr = 0x55; + } + } + break; + + case 2: + /* Pattern: 00 01 02 03 04 05 + * ... FE FF 00 01.. + */ + for (ii=0; ii < size; ii++, ptr++) + *ptr = (char) ii; + break; + + case 3: + if (width) { + /* Wide Pattern: FFFE 0001 FFFD 0002 + * ... 4000 DFFF 8000 EFFF + */ + byte = 0; + for (ii=0; ii < size/2; ii++) { + /* Create the base pattern + */ + val = (1 << byte); + /* every 64 (0x40) bytes flip the pattern + * since we fill 2 bytes / iteration, + * test for ii = 0x20 + */ + if (ii & 0x20) + val = ~(val); + + if (ii & 0x01) { + *ptr = (char)( (val & 0xFF00) >> 8); + ptr++; + *ptr = (char)(val & 0xFF); + byte++; + byte &= 0x0F; + } else { + val = ~val; + *ptr = (char)( (val & 0xFF00) >> 8); + ptr++; + *ptr = (char)(val & 0xFF); + } + + ptr++; + } + } else { + /* Narrow Pattern: FE 01 FD 02 FB 04 + * .. 7F 80 01 FE 02 FD ... 80 7F + */ + byte = 0; + for (ii=0; ii < size; ii++, ptr++) { + /* Base pattern - first 32 bytes + */ + if (ii & 0x01) { + *ptr = (1 << byte); + byte++; + byte &= 0x07; + } else { + *ptr = (char) (~(1 << byte)); + } + + /* Flip the pattern every 32 bytes + */ + if (ii & 0x20) + *ptr = ~(*ptr); + } + } + break; + } +} +#endif /* ~MPTSCSIH_DISABLE_DOMAIN_VALIDATION */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* Commandline Parsing routines and defines. + * + * insmod format: + * insmod mptscsih mptscsih="width:1 dv:n factor:0x09" + * boot format: + * mptscsih=width:1,dv:n,factor:0x8 + * + */ +#ifdef MODULE +#define ARG_SEP ' ' +#else +#define ARG_SEP ',' +#endif + +static char setup_token[] __initdata = + "dv:" + "width:" + "factor:" + ; /* DONNOT REMOVE THIS ';' */ + +#define OPT_DV 1 +#define OPT_MAX_WIDTH 2 +#define OPT_MIN_SYNC_FACTOR 3 + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +__init get_setup_token(char *p) +{ + char *cur = setup_token; + char *pc; + int i = 0; + + while (cur != NULL && (pc = strchr(cur, ':')) != NULL) { + ++pc; + ++i; + if (!strncmp(p, cur, pc - cur)) + return i; + cur = pc; + } + return 0; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +static int +__init mptscsih_setup(char *str) +{ + char *cur = str; + char *pc, *pv; + unsigned long val; + int c; + + while (cur != NULL && (pc = strchr(cur, ':')) != NULL) { + char *pe; + + val = 0; + pv = pc; + c = *++pv; + + if (c == 'n') + val = 0; + else if (c == 'y') + val = 1; + else + val = (int) simple_strtoul(pv, &pe, 0); + + printk("Found Token: %s, value %x\n", cur, (int)val); + switch (get_setup_token(cur)) { + case OPT_DV: + driver_setup.dv = val; + break; + + case OPT_MAX_WIDTH: + driver_setup.max_width = val; + break; + + case OPT_MIN_SYNC_FACTOR: + driver_setup.min_sync_fac = val; + break; + + default: + printk("mptscsih_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur); + break; + } + + if ((cur = strchr(cur, ARG_SEP)) != NULL) + ++cur; + } + return 1; +} + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + diff --git a/xen/drivers/message/fusion/mptscsih.h b/xen/drivers/message/fusion/mptscsih.h new file mode 100644 index 0000000000..eaa75beb3d --- /dev/null +++ b/xen/drivers/message/fusion/mptscsih.h @@ -0,0 +1,343 @@ +/* + * linux/drivers/message/fusion/mptscsih.h + * High performance SCSI / Fibre Channel SCSI Host device driver. + * For use with PCI chip/adapter(s): + * LSIFC9xx/LSI409xx Fibre Channel + * running LSI Logic Fusion MPT (Message Passing Technology) firmware. + * + * Credits: + * This driver would not exist if not for Alan Cox's development + * of the linux i2o driver. + * + * A huge debt of gratitude is owed to David S. Miller (DaveM) + * for fixing much of the stupid and broken stuff in the early + * driver while porting to sparc64 platform. THANK YOU! + * + * (see also mptbase.c) + * + * Copyright (c) 1999-2002 LSI Logic Corporation + * Originally By: Steven J. Ralston + * (mailto:sjralston1@netscape.net) + * (mailto:lstephens@lsil.com) + * + * $Id: mptscsih.h,v 1.1.2.2 2003/05/07 14:08:35 pdelaney Exp $ + */ +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + NO WARRANTY + THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT + LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, + MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is + solely responsible for determining the appropriateness of using and + distributing the Program and assumes all risks associated with its + exercise of rights under this Agreement, including but not limited to + the risks and costs of program errors, damage to or loss of data, + programs or equipment, and unavailability or interruption of operations. + + DISCLAIMER OF LIABILITY + NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR + TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED + HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef SCSIHOST_H_INCLUDED +#define SCSIHOST_H_INCLUDED +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include +#include + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * SCSI Public stuff... + */ + +/* + * Try to keep these at 2^N-1 + */ +#define MPT_FC_CAN_QUEUE 127 +#if defined MPT_SCSI_USE_NEW_EH + #define MPT_SCSI_CAN_QUEUE 127 +#else + #define MPT_SCSI_CAN_QUEUE 63 +#endif + +#define MPT_SCSI_CMD_PER_DEV_HIGH 31 +#define MPT_SCSI_CMD_PER_DEV_LOW 7 + +#define MPT_SCSI_CMD_PER_LUN 7 + +#define MPT_SCSI_MAX_SECTORS 8192 + +/* + * Set the MAX_SGE value based on user input. + */ +#ifdef CONFIG_FUSION_MAX_SGE +#if CONFIG_FUSION_MAX_SGE < 16 +#define MPT_SCSI_SG_DEPTH 16 +#elif CONFIG_FUSION_MAX_SGE > 128 +#define MPT_SCSI_SG_DEPTH 128 +#else +#define MPT_SCSI_SG_DEPTH CONFIG_FUSION_MAX_SGE +#endif +#else +#define MPT_SCSI_SG_DEPTH 40 +#endif + +/* To disable domain validation, uncomment the + * following line. No effect for FC devices. + * For SCSI devices, driver will negotiate to + * NVRAM settings (if available) or to maximum adapter + * capabilities. + */ +/* #define MPTSCSIH_DISABLE_DOMAIN_VALIDATION */ + + +/* SCSI driver setup structure. Settings can be overridden + * by command line options. + */ +#define MPTSCSIH_DOMAIN_VALIDATION 1 +#define MPTSCSIH_MAX_WIDTH 1 +#define MPTSCSIH_MIN_SYNC 0x08 + +struct mptscsih_driver_setup +{ + u8 dv; + u8 max_width; + u8 min_sync_fac; +}; + + +#define MPTSCSIH_DRIVER_SETUP \ +{ \ + MPTSCSIH_DOMAIN_VALIDATION, \ + MPTSCSIH_MAX_WIDTH, \ + MPTSCSIH_MIN_SYNC, \ +} + + + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * Various bits and pieces broke within the lk-2.4.0-testN series:-( + * So here are various HACKS to work around them. + */ + +/* + * Conditionalizing with "#ifdef MODULE/#endif" around: + * static Scsi_Host_Template driver_template = XX; + * #include <../../scsi/scsi_module.c> + * lines was REMOVED @ lk-2.4.0-test9 + * Issue discovered 20001213 by: sshirron + */ +#define MPT_SCSIHOST_NEED_ENTRY_EXIT_HOOKUPS 1 +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,4,0) +# if LINUX_VERSION_CODE == KERNEL_VERSION(2,4,0) + /* + * Super HACK! -by sralston:-( + * (good grief; heaven help me!) + */ +# include +# if !defined(CAP_LEASE) && !defined(MODULE) +# undef MPT_SCSIHOST_NEED_ENTRY_EXIT_HOOKUPS +# endif +# else +# ifndef MODULE +# undef MPT_SCSIHOST_NEED_ENTRY_EXIT_HOOKUPS +# endif +# endif +#endif + +/* + * tq_scheduler disappeared @ lk-2.4.0-test12 + * (right when newly defined TQ_ACTIVE) + * tq_struct reworked in 2.5.41. Include workqueue.h. + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,41) +# include +# include +#define SCHEDULE_TASK(x) \ + if (schedule_work(x) == 0) { \ + /*MOD_DEC_USE_COUNT*/; \ + } +#else +#define HAVE_TQ_SCHED 1 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0) +# include +# ifdef TQ_ACTIVE +# undef HAVE_TQ_SCHED +# endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,40) +# undef HAVE_TQ_SCHED +#endif +#endif +#ifdef HAVE_TQ_SCHED +#define SCHEDULE_TASK(x) \ + /*MOD_INC_USE_COUNT*/; \ + (x)->next = NULL; \ + queue_task(x, &tq_scheduler) +#else + +/* SAE: mdelay */ +#define SCHEDULE_TASK(x) mdelay(100) + +#if XENO_KILLED + +#define SCHEDULE_TASK(x) \ + *MOD_INC_USE_COUNT*; \ + if (schedule_task(x) == 0) { \ + *MOD_DEC_USE_COUNT*; + +#endif + +#endif +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#define x_scsi_detect mptscsih_detect +#define x_scsi_release mptscsih_release +#define x_scsi_info mptscsih_info +#define x_scsi_queuecommand mptscsih_qcmd +#define x_scsi_abort mptscsih_abort +#define x_scsi_bus_reset mptscsih_bus_reset +#define x_scsi_dev_reset mptscsih_dev_reset +#define x_scsi_host_reset mptscsih_host_reset +#define x_scsi_bios_param mptscsih_bios_param + +#define x_scsi_taskmgmt_bh mptscsih_taskmgmt_bh +#define x_scsi_old_abort mptscsih_old_abort +#define x_scsi_old_reset mptscsih_old_reset +#define x_scsi_select_queue_depths mptscsih_select_queue_depths + +/* SAE: No proc */ + +#if defined(CONFIG_PROC_FS) +#define x_scsi_proc_info mptscsih_proc_info +#else +#define x_scsi_proc_info NULL +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ +/* + * MPT SCSI Host / Initiator decls... + */ +extern int x_scsi_detect(Scsi_Host_Template *); +extern int x_scsi_release(struct Scsi_Host *host); +extern const char *x_scsi_info(struct Scsi_Host *); +extern int x_scsi_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)); +#ifdef MPT_SCSI_USE_NEW_EH +extern int x_scsi_abort(Scsi_Cmnd *); +extern int x_scsi_bus_reset(Scsi_Cmnd *); +extern int x_scsi_dev_reset(Scsi_Cmnd *); +extern int x_scsi_host_reset(Scsi_Cmnd *); +#else +extern int x_scsi_old_abort(Scsi_Cmnd *); +extern int x_scsi_old_reset(Scsi_Cmnd *, unsigned int); +#endif +extern int x_scsi_bios_param(Disk *, kdev_t, int *); +extern void x_scsi_taskmgmt_bh(void *); +extern void x_scsi_select_queue_depths(struct Scsi_Host *, Scsi_Device *); + +/* SAE: No proc */ + +#if defined(CONFIG_PROC_FS) + +extern int x_scsi_proc_info(char *, char **, off_t, int, int, int); + +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +#define PROC_SCSI_DECL +#else +#define PROC_SCSI_DECL .proc_name = "mptscsih", +#endif + +#ifdef MPT_SCSI_USE_NEW_EH +#define MPT_SCSIHOST { \ + .next = NULL, \ + PROC_SCSI_DECL \ + .proc_info = x_scsi_proc_info, \ + .name = "MPT SCSI Host", \ + .detect = x_scsi_detect, \ + .release = x_scsi_release, \ + .info = x_scsi_info, \ + .command = NULL, \ + .queuecommand = x_scsi_queuecommand, \ + .eh_strategy_handler = NULL, \ + .eh_abort_handler = x_scsi_abort, \ + .eh_device_reset_handler = x_scsi_dev_reset, \ + .eh_bus_reset_handler = x_scsi_bus_reset, \ + .eh_host_reset_handler = NULL, \ + .bios_param = x_scsi_bios_param, \ + .can_queue = MPT_SCSI_CAN_QUEUE, \ + .this_id = -1, \ + .sg_tablesize = MPT_SCSI_SG_DEPTH, \ + .cmd_per_lun = MPT_SCSI_CMD_PER_LUN, \ + .unchecked_isa_dma = 0, \ + .use_clustering = ENABLE_CLUSTERING, \ + .use_new_eh_code = 1 \ +} + +#else /* MPT_SCSI_USE_NEW_EH */ + +#define MPT_SCSIHOST { \ + .next = NULL, \ + PROC_SCSI_DECL \ + .name = "MPT SCSI Host", \ + .detect = x_scsi_detect, \ + .release = x_scsi_release, \ + .info = x_scsi_info, \ + .command = NULL, \ + .queuecommand = x_scsi_queuecommand, \ + .abort = x_scsi_old_abort, \ + .reset = x_scsi_old_reset, \ + .bios_param = x_scsi_bios_param, \ + .can_queue = MPT_SCSI_CAN_QUEUE, \ + .this_id = -1, \ + .sg_tablesize = MPT_SCSI_SG_DEPTH, \ + .cmd_per_lun = MPT_SCSI_CMD_PER_LUN, \ + .unchecked_isa_dma = 0, \ + .use_clustering = ENABLE_CLUSTERING \ +} +#endif /* MPT_SCSI_USE_NEW_EH */ + + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +/* include/scsi/scsi.h may not be quite complete... */ +#ifndef RESERVE_10 +#define RESERVE_10 0x56 +#endif +#ifndef RELEASE_10 +#define RELEASE_10 0x57 +#endif +#ifndef PERSISTENT_RESERVE_IN +#define PERSISTENT_RESERVE_IN 0x5e +#endif +#ifndef PERSISTENT_RESERVE_OUT +#define PERSISTENT_RESERVE_OUT 0x5f +#endif + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#endif + diff --git a/xen/drivers/message/fusion/scsi3.h b/xen/drivers/message/fusion/scsi3.h new file mode 100644 index 0000000000..9160ba5e78 --- /dev/null +++ b/xen/drivers/message/fusion/scsi3.h @@ -0,0 +1,707 @@ +/* + * linux/drivers/message/fusion/scsi3.h + * SCSI-3 definitions and macros. + * (Ultimately) SCSI-3 definitions; for now, inheriting + * SCSI-2 definitions. + * + * Copyright (c) 1996-2002 Steven J. Ralston + * Written By: Steven J. Ralston (19960517) + * (mailto:sjralston1@netscape.net) + * (mailto:lstephens@lsil.com) + * + * $Id: scsi3.h,v 1.9 2002/02/27 18:45:02 sralston Exp $ + */ + +#ifndef SCSI3_H_INCLUDED +#define SCSI3_H_INCLUDED +/***************************************************************************/ + +/**************************************************************************** + * + * Includes + */ +#ifdef __KERNEL__ +#include +#else + #ifndef U_STUFF_DEFINED + #define U_STUFF_DEFINED + typedef unsigned char u8; + typedef unsigned short u16; + typedef unsigned int u32; + #endif +#endif + +/**************************************************************************** + * + * Defines + */ + +/* + * SCSI Commands + */ +#define CMD_TestUnitReady 0x00 +#define CMD_RezeroUnit 0x01 /* direct-access devices */ +#define CMD_Rewind 0x01 /* sequential-access devices */ +#define CMD_RequestSense 0x03 +#define CMD_FormatUnit 0x04 +#define CMD_ReassignBlock 0x07 +#define CMD_Read6 0x08 +#define CMD_Write6 0x0A +#define CMD_WriteFilemark 0x10 +#define CMD_Space 0x11 +#define CMD_Inquiry 0x12 +#define CMD_ModeSelect6 0x15 +#define CMD_ModeSense6 0x1A +#define CMD_Reserve6 0x16 +#define CMD_Release6 0x17 +#define CMD_Erase 0x19 +#define CMD_StartStopUnit 0x1b /* direct-access devices */ +#define CMD_LoadUnload 0x1b /* sequential-access devices */ +#define CMD_ReceiveDiagnostic 0x1C +#define CMD_SendDiagnostic 0x1D +#define CMD_ReadCapacity 0x25 +#define CMD_Read10 0x28 +#define CMD_Write10 0x2A +#define CMD_WriteVerify 0x2E +#define CMD_Verify 0x2F +#define CMD_SynchronizeCache 0x35 +#define CMD_ReadDefectData 0x37 +#define CMD_WriteBuffer 0x3B +#define CMD_ReadBuffer 0x3C +#define CMD_ReadLong 0x3E +#define CMD_LogSelect 0x4C +#define CMD_LogSense 0x4D +#define CMD_ModeSelect10 0x55 +#define CMD_Reserve10 0x56 +#define CMD_Release10 0x57 +#define CMD_ModeSense10 0x5A +#define CMD_PersistReserveIn 0x5E +#define CMD_PersistReserveOut 0x5F +#define CMD_ReportLuns 0xA0 + +/* + * Control byte field + */ +#define CONTROL_BYTE_NACA_BIT 0x04 +#define CONTROL_BYTE_Flag_BIT 0x02 +#define CONTROL_BYTE_Link_BIT 0x01 + +/* + * SCSI Messages + */ +#define MSG_COMPLETE 0x00 +#define MSG_EXTENDED 0x01 +#define MSG_SAVE_POINTERS 0x02 +#define MSG_RESTORE_POINTERS 0x03 +#define MSG_DISCONNECT 0x04 +#define MSG_IDERROR 0x05 +#define MSG_ABORT 0x06 +#define MSG_REJECT 0x07 +#define MSG_NOP 0x08 +#define MSG_PARITY_ERROR 0x09 +#define MSG_LINKED_CMD_COMPLETE 0x0a +#define MSG_LCMD_COMPLETE_W_FLG 0x0b +#define MSG_BUS_DEVICE_RESET 0x0c +#define MSG_ABORT_TAG 0x0d +#define MSG_CLEAR_QUEUE 0x0e +#define MSG_INITIATE_RECOVERY 0x0f + +#define MSG_RELEASE_RECOVRY 0x10 +#define MSG_TERMINATE_IO 0x11 + +#define MSG_SIMPLE_QUEUE 0x20 +#define MSG_HEAD_OF_QUEUE 0x21 +#define MSG_ORDERED_QUEUE 0x22 +#define MSG_IGNORE_WIDE_RESIDUE 0x23 + +#define MSG_IDENTIFY 0x80 +#define MSG_IDENTIFY_W_DISC 0xc0 + +/* + * SCSI Phases + */ +#define PHS_DATA_OUT 0x00 +#define PHS_DATA_IN 0x01 +#define PHS_COMMAND 0x02 +#define PHS_STATUS 0x03 +#define PHS_MSG_OUT 0x06 +#define PHS_MSG_IN 0x07 + +/* + * Statuses + */ +#define STS_GOOD 0x00 +#define STS_CHECK_CONDITION 0x02 +#define STS_CONDITION_MET 0x04 +#define STS_BUSY 0x08 +#define STS_INTERMEDIATE 0x10 +#define STS_INTERMEDIATE_CONDITION_MET 0x14 +#define STS_RESERVATION_CONFLICT 0x18 +#define STS_COMMAND_TERMINATED 0x22 +#define STS_TASK_SET_FULL 0x28 +#define STS_QUEUE_FULL 0x28 +#define STS_ACA_ACTIVE 0x30 + +#define STS_VALID_MASK 0x3e + +#define SCSI_STATUS(x) ((x) & STS_VALID_MASK) + +/* + * SCSI QTag Types + */ +#define QTAG_SIMPLE 0x20 +#define QTAG_HEAD_OF_Q 0x21 +#define QTAG_ORDERED 0x22 + +/* + * SCSI Sense Key Definitons + */ +#define SK_NO_SENSE 0x00 +#define SK_RECOVERED_ERROR 0x01 +#define SK_NOT_READY 0x02 +#define SK_MEDIUM_ERROR 0x03 +#define SK_HARDWARE_ERROR 0x04 +#define SK_ILLEGAL_REQUEST 0x05 +#define SK_UNIT_ATTENTION 0x06 +#define SK_DATA_PROTECT 0x07 +#define SK_BLANK_CHECK 0x08 +#define SK_VENDOR_SPECIFIC 0x09 +#define SK_COPY_ABORTED 0x0a +#define SK_ABORTED_COMMAND 0x0b +#define SK_EQUAL 0x0c +#define SK_VOLUME_OVERFLOW 0x0d +#define SK_MISCOMPARE 0x0e +#define SK_RESERVED 0x0f + + + +#define SCSI_MAX_INQUIRY_BYTES 96 +#define SCSI_STD_INQUIRY_BYTES 36 + +#undef USE_SCSI_COMPLETE_INQDATA +/* + * Structure definition for SCSI Inquiry Data + * + * NOTE: The following structure is 96 bytes in size + * iff USE_SCSI_COMPLETE_INQDATA IS defined above (i.e. w/ "#define"). + * If USE_SCSI_COMPLETE_INQDATA is NOT defined above (i.e. w/ "#undef") + * then the following structure is only 36 bytes in size. + * THE CHOICE IS YOURS! + */ +typedef struct SCSI_Inquiry_Data +{ +#ifdef USE_SCSI_COMPLETE_INQDATA + u8 InqByte[SCSI_MAX_INQUIRY_BYTES]; +#else + u8 InqByte[SCSI_STD_INQUIRY_BYTES]; +#endif + +/* + * the following structure works only for little-endian (Intel, + * LSB first (1234) byte order) systems with 4-byte ints. + * + u32 Periph_Device_Type : 5, + Periph_Qualifier : 3, + Device_Type_Modifier : 7, + Removable_Media : 1, + ANSI_Version : 3, + ECMA_Version : 3, + ISO_Version : 2, + Response_Data_Format : 4, + reserved_0 : 3, + AERC : 1 ; + u32 Additional_Length : 8, + reserved_1 :16, + SftReset : 1, + CmdQue : 1, + reserved_2 : 1, + Linked : 1, + Sync : 1, + WBus16 : 1, + WBus32 : 1, + RelAdr : 1 ; + u8 Vendor_ID[8]; + u8 Product_ID[16]; + u8 Revision_Level [4]; +#ifdef USE_SCSI_COMPLETE_INQDATA + u8 Vendor_Specific[20]; + u8 reserved_3[40]; +#endif + * + */ + +} SCSI_Inquiry_Data_t; + +#define INQ_PERIPHINFO_BYTE 0 +#define INQ_Periph_Qualifier_MASK 0xe0 +#define INQ_Periph_Device_Type_MASK 0x1f + +#define INQ_Peripheral_Qualifier(inqp) \ + (int)((*((u8*)(inqp)+INQ_PERIPHINFO_BYTE) & INQ_Periph_Qualifier_MASK) >> 5) +#define INQ_Peripheral_Device_Type(inqp) \ + (int)(*((u8*)(inqp)+INQ_PERIPHINFO_BYTE) & INQ_Periph_Device_Type_MASK) + + +#define INQ_DEVTYPEMOD_BYTE 1 +#define INQ_RMB_BIT 0x80 +#define INQ_Device_Type_Modifier_MASK 0x7f + +#define INQ_Removable_Medium(inqp) \ + (int)(*((u8*)(inqp)+INQ_DEVTYPEMOD_BYTE) & INQ_RMB_BIT) +#define INQ_Device_Type_Modifier(inqp) \ + (int)(*((u8*)(inqp)+INQ_DEVTYPEMOD_BYTE) & INQ_Device_Type_Modifier_MASK) + + +#define INQ_VERSIONINFO_BYTE 2 +#define INQ_ISO_Version_MASK 0xc0 +#define INQ_ECMA_Version_MASK 0x38 +#define INQ_ANSI_Version_MASK 0x07 + +#define INQ_ISO_Version(inqp) \ + (int)(*((u8*)(inqp)+INQ_VERSIONINFO_BYTE) & INQ_ISO_Version_MASK) +#define INQ_ECMA_Version(inqp) \ + (int)(*((u8*)(inqp)+INQ_VERSIONINFO_BYTE) & INQ_ECMA_Version_MASK) +#define INQ_ANSI_Version(inqp) \ + (int)(*((u8*)(inqp)+INQ_VERSIONINFO_BYTE) & INQ_ANSI_Version_MASK) + + +#define INQ_BYTE3 3 +#define INQ_AERC_BIT 0x80 +#define INQ_TrmTsk_BIT 0x40 +#define INQ_NormACA_BIT 0x20 +#define INQ_RDF_MASK 0x0F + +#define INQ_AER_Capable(inqp) \ + (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_AERC_BIT) +#define INQ_TrmTsk(inqp) \ + (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_TrmTsk_BIT) +#define INQ_NormACA(inqp) \ + (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_NormACA_BIT) +#define INQ_Response_Data_Format(inqp) \ + (int)(*((u8*)(inqp)+INQ_BYTE3) & INQ_RDF_MASK) + + +#define INQ_CAPABILITY_BYTE 7 +#define INQ_RelAdr_BIT 0x80 +#define INQ_WBus32_BIT 0x40 +#define INQ_WBus16_BIT 0x20 +#define INQ_Sync_BIT 0x10 +#define INQ_Linked_BIT 0x08 + /* INQ_Reserved BIT 0x40 */ +#define INQ_CmdQue_BIT 0x02 +#define INQ_SftRe_BIT 0x01 + +#define IS_RelAdr_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_RelAdr_BIT) +#define IS_WBus32_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_WBus32_BIT) +#define IS_WBus16_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_WBus16_BIT) +#define IS_Sync_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_Sync_BIT) +#define IS_Linked_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_Linked_BIT) +#define IS_CmdQue_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_CmdQue_BIT) +#define IS_SftRe_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_SftRe_BIT) + +#define INQ_Width_BITS \ + (INQ_WBus32_BIT | INQ_WBus16_BIT) +#define IS_Wide_DEV(inqp) \ + (int)(*((u8*)(inqp)+INQ_CAPABILITY_BYTE) & INQ_Width_BITS) + + +/* + * SCSI peripheral device types + */ +#define SCSI_TYPE_DAD 0x00 /* Direct Access Device */ +#define SCSI_TYPE_SAD 0x01 /* Sequential Access Device */ +#define SCSI_TYPE_TAPE SCSI_TYPE_SAD +#define SCSI_TYPE_PRT 0x02 /* Printer */ +#define SCSI_TYPE_PROC 0x03 /* Processor */ +#define SCSI_TYPE_WORM 0x04 +#define SCSI_TYPE_CDROM 0x05 +#define SCSI_TYPE_SCAN 0x06 /* Scanner */ +#define SCSI_TYPE_OPTICAL 0x07 /* Magneto/Optical */ +#define SCSI_TYPE_CHANGER 0x08 +#define SCSI_TYPE_COMM 0x09 /* Communications device */ +#define SCSI_TYPE_UNKNOWN 0x1f +#define SCSI_TYPE_UNCONFIGURED_LUN 0x7f + +#define SCSI_TYPE_MAX_KNOWN SCSI_TYPE_COMM + +/* + * Peripheral Qualifiers + */ +#define DEVICE_PRESENT 0x00 +#define LUN_NOT_PRESENT 0x01 +#define LUN_NOT_SUPPORTED 0x03 + +/* + * ANSI Versions + */ +#ifndef SCSI_1 +#define SCSI_1 0x01 +#endif +#ifndef SCSI_2 +#define SCSI_2 0x02 +#endif +#ifndef SCSI_3 +#define SCSI_3 0x03 +#endif + + +#define SCSI_MAX_SENSE_BYTES 255 +#define SCSI_STD_SENSE_BYTES 18 +#define SCSI_PAD_SENSE_BYTES (SCSI_MAX_SENSE_BYTES - SCSI_STD_SENSE_BYTES) + +#undef USE_SCSI_COMPLETE_SENSE +/* + * Structure definition for SCSI Sense Data + * + * NOTE: The following structure is 255 bytes in size + * iiff USE_SCSI_COMPLETE_SENSE IS defined above (i.e. w/ "#define"). + * If USE_SCSI_COMPLETE_SENSE is NOT defined above (i.e. w/ "#undef") + * then the following structure is only 19 bytes in size. + * THE CHOICE IS YOURS! + * + */ +typedef struct SCSI_Sense_Data +{ +#ifdef USE_SCSI_COMPLETE_SENSE + u8 SenseByte[SCSI_MAX_SENSE_BYTES]; +#else + u8 SenseByte[SCSI_STD_SENSE_BYTES]; +#endif + +/* + * the following structure works only for little-endian (Intel, + * LSB first (1234) byte order) systems with 4-byte ints. + * + u8 Error_Code :4, // 0x00 + Error_Class :3, + Valid :1 + ; + u8 Segment_Number // 0x01 + ; + u8 Sense_Key :4, // 0x02 + Reserved :1, + Incorrect_Length_Indicator:1, + End_Of_Media :1, + Filemark :1 + ; + u8 Information_MSB; // 0x03 + u8 Information_Byte2; // 0x04 + u8 Information_Byte1; // 0x05 + u8 Information_LSB; // 0x06 + u8 Additional_Length; // 0x07 + + u32 Command_Specific_Information; // 0x08 - 0x0b + + u8 Additional_Sense_Code; // 0x0c + u8 Additional_Sense_Code_Qualifier; // 0x0d + u8 Field_Replaceable_Unit_Code; // 0x0e + u8 Illegal_Req_Bit_Pointer :3, // 0x0f + Illegal_Req_Bit_Valid :1, + Illegal_Req_Reserved :2, + Illegal_Req_Cmd_Data :1, + Sense_Key_Specific_Valid :1 + ; + u16 Sense_Key_Specific_Data; // 0x10 - 0x11 + +#ifdef USE_SCSI_COMPLETE_SENSE + u8 Additional_Sense_Data[SCSI_PAD_SENSE_BYTES]; +#else + u8 Additional_Sense_Data[1]; +#endif + * + */ + +} SCSI_Sense_Data_t; + + +#define SD_ERRCODE_BYTE 0 +#define SD_Valid_BIT 0x80 +#define SD_Error_Code_MASK 0x7f +#define SD_Valid(sdp) \ + (int)(*((u8*)(sdp)+SD_ERRCODE_BYTE) & SD_Valid_BIT) +#define SD_Error_Code(sdp) \ + (int)(*((u8*)(sdp)+SD_ERRCODE_BYTE) & SD_Error_Code_MASK) + + +#define SD_SEGNUM_BYTE 1 +#define SD_Segment_Number(sdp) (int)(*((u8*)(sdp)+SD_SEGNUM_BYTE)) + + +#define SD_SENSEKEY_BYTE 2 +#define SD_Filemark_BIT 0x80 +#define SD_EOM_BIT 0x40 +#define SD_ILI_BIT 0x20 +#define SD_Sense_Key_MASK 0x0f +#define SD_Filemark(sdp) \ + (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_Filemark_BIT) +#define SD_EOM(sdp) \ + (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_EOM_BIT) +#define SD_ILI(sdp) \ + (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_ILI_BIT) +#define SD_Sense_Key(sdp) \ + (int)(*((u8*)(sdp)+SD_SENSEKEY_BYTE) & SD_Sense_Key_MASK) + + +#define SD_INFO3_BYTE 3 +#define SD_INFO2_BYTE 4 +#define SD_INFO1_BYTE 5 +#define SD_INFO0_BYTE 6 +#define SD_Information3(sdp) (int)(*((u8*)(sdp)+SD_INFO3_BYTE)) +#define SD_Information2(sdp) (int)(*((u8*)(sdp)+SD_INFO2_BYTE)) +#define SD_Information1(sdp) (int)(*((u8*)(sdp)+SD_INFO1_BYTE)) +#define SD_Information0(sdp) (int)(*((u8*)(sdp)+SD_INFO0_BYTE)) + + +#define SD_ADDL_LEN_BYTE 7 +#define SD_Additional_Sense_Length(sdp) \ + (int)(*((u8*)(sdp)+SD_ADDL_LEN_BYTE)) +#define SD_Addl_Sense_Len SD_Additional_Sense_Length + + +#define SD_CMD_SPECIFIC3_BYTE 8 +#define SD_CMD_SPECIFIC2_BYTE 9 +#define SD_CMD_SPECIFIC1_BYTE 10 +#define SD_CMD_SPECIFIC0_BYTE 11 +#define SD_Cmd_Specific_Info3(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC3_BYTE)) +#define SD_Cmd_Specific_Info2(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC2_BYTE)) +#define SD_Cmd_Specific_Info1(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC1_BYTE)) +#define SD_Cmd_Specific_Info0(sdp) (int)(*((u8*)(sdp)+SD_CMD_SPECIFIC0_BYTE)) + + +#define SD_ADDL_SENSE_CODE_BYTE 12 +#define SD_Additional_Sense_Code(sdp) \ + (int)(*((u8*)(sdp)+SD_ADDL_SENSE_CODE_BYTE)) +#define SD_Addl_Sense_Code SD_Additional_Sense_Code +#define SD_ASC SD_Additional_Sense_Code + + +#define SD_ADDL_SENSE_CODE_QUAL_BYTE 13 +#define SD_Additional_Sense_Code_Qualifier(sdp) \ + (int)(*((u8*)(sdp)+SD_ADDL_SENSE_CODE_QUAL_BYTE)) +#define SD_Addl_Sense_Code_Qual SD_Additional_Sense_Code_Qualifier +#define SD_ASCQ SD_Additional_Sense_Code_Qualifier + + +#define SD_FIELD_REPL_UNIT_CODE_BYTE 14 +#define SD_Field_Replaceable_Unit_Code(sdp) \ + (int)(*((u8*)(sdp)+SD_FIELD_REPL_UNIT_CODE_BYTE)) +#define SD_Field_Repl_Unit_Code SD_Field_Replaceable_Unit_Code +#define SD_FRUC SD_Field_Replaceable_Unit_Code +#define SD_FRU SD_Field_Replaceable_Unit_Code + + +/* + * Sense-Key Specific offsets and macros. + */ +#define SD_SKS2_BYTE 15 +#define SD_SKS_Valid_BIT 0x80 +#define SD_SKS_Cmd_Data_BIT 0x40 +#define SD_SKS_Bit_Ptr_Valid_BIT 0x08 +#define SD_SKS_Bit_Ptr_MASK 0x07 +#define SD_SKS1_BYTE 16 +#define SD_SKS0_BYTE 17 +#define SD_Sense_Key_Specific_Valid(sdp) \ + (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Valid_BIT) +#define SD_SKS_Valid SD_Sense_Key_Specific_Valid +#define SD_SKS_CDB_Error(sdp) \ + (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Cmd_Data_BIT) +#define SD_Was_Illegal_Request SD_SKS_CDB_Error +#define SD_SKS_Bit_Pointer_Valid(sdp) \ + (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Bit_Ptr_Valid_BIT) +#define SD_SKS_Bit_Pointer(sdp) \ + (int)(*((u8*)(sdp)+SD_SKS2_BYTE) & SD_SKS_Bit_Ptr_MASK) +#define SD_Field_Pointer(sdp) \ + (int)( ((u16)(*((u8*)(sdp)+SD_SKS1_BYTE)) << 8) \ + + *((u8*)(sdp)+SD_SKS0_BYTE) ) +#define SD_Bad_Byte SD_Field_Pointer +#define SD_Actual_Retry_Count SD_Field_Pointer +#define SD_Progress_Indication SD_Field_Pointer + +/* + * Mode Sense Write Protect Mask + */ +#define WRITE_PROTECT_MASK 0X80 + +/* + * Medium Type Codes + */ +#define OPTICAL_DEFAULT 0x00 +#define OPTICAL_READ_ONLY_MEDIUM 0x01 +#define OPTICAL_WRITE_ONCE_MEDIUM 0x02 +#define OPTICAL_READ_WRITABLE_MEDIUM 0x03 +#define OPTICAL_RO_OR_WO_MEDIUM 0x04 +#define OPTICAL_RO_OR_RW_MEDIUM 0x05 +#define OPTICAL_WO_OR_RW_MEDIUM 0x06 + + + +/* + * Structure definition for READ6, WRITE6 (6-byte CDB) + */ +typedef struct SCSI_RW6_CDB +{ + u32 OpCode :8, + LBA_HI :5, /* 5 MSBit's of the LBA */ + Lun :3, + LBA_MID :8, /* NOTE: total of 21 bits in LBA */ + LBA_LO :8 ; /* Max LBA = 0x001fffff */ + u8 BlockCount; + u8 Control; +} SCSI_RW6_t; + +#define MAX_RW6_LBA ((u32)0x001fffff) + +/* + * Structure definition for READ10, WRITE10 (10-byte CDB) + * + * NOTE: ParityCheck bit is applicable only for VERIFY and WRITE VERIFY for + * the ADP-92 DAC only. In the SCSI2 spec. this same bit is defined as a + * FUA (forced unit access) bit for READs and WRITEs. Since this driver + * does not use the FUA, this bit is defined as it is used by the ADP-92. + * Also, for READ CAPACITY, only the OpCode field is used. + */ +typedef struct SCSI_RW10_CDB +{ + u8 OpCode; + u8 Reserved1; + u32 LBA; + u8 Reserved2; + u16 BlockCount; + u8 Control; +} SCSI_RW10_t; + +#define PARITY_CHECK 0x08 /* parity check bit - byte[1], bit 3 */ + + /* + * Structure definition for data returned by READ CAPACITY cmd; + * READ CAPACITY data + */ + typedef struct READ_CAP_DATA + { + u32 MaxLBA; + u32 BlockBytes; + } SCSI_READ_CAP_DATA_t, *pSCSI_READ_CAP_DATA_t; + + +/* + * Structure definition for FORMAT UNIT CDB (6-byte CDB) + */ +typedef struct _SCSI_FORMAT_UNIT +{ + u8 OpCode; + u8 Reserved1; + u8 VendorSpecific; + u16 Interleave; + u8 Control; +} SCSI_FORMAT_UNIT_t; + +/* + * Structure definition for REQUEST SENSE (6-byte CDB) + */ +typedef struct _SCSI_REQUEST_SENSE +{ + u8 OpCode; + u8 Reserved1; + u8 Reserved2; + u8 Reserved3; + u8 AllocLength; + u8 Control; +} SCSI_REQ_SENSE_t; + +/* + * Structure definition for REPORT LUNS (12-byte CDB) + */ +typedef struct _SCSI_REPORT_LUNS +{ + u8 OpCode; + u8 Reserved1[5]; + u32 AllocationLength; + u8 Reserved2; + u8 Control; +} SCSI_REPORT_LUNS_t, *pSCSI_REPORT_LUNS_t; + + /* + * (per-level) LUN information bytes + */ +/* + * Following doesn't work on ARMCC compiler + * [apparently] because it pads every struct + * to be multiple of 4 bytes! + * So SCSI_LUN_LEVELS_t winds up being 16 + * bytes instead of 8! + * + typedef struct LUN_INFO + { + u8 AddrMethod_plus_LunOrBusNumber; + u8 LunOrTarget; + } SCSI_LUN_INFO_t, *pSCSI_LUN_INFO_t; + + typedef struct LUN_LEVELS + { + SCSI_LUN_INFO_t LUN_0; + SCSI_LUN_INFO_t LUN_1; + SCSI_LUN_INFO_t LUN_2; + SCSI_LUN_INFO_t LUN_3; + } SCSI_LUN_LEVELS_t, *pSCSI_LUN_LEVELS_t; +*/ + /* + * All 4 levels (8 bytes) of LUN information + */ + typedef struct LUN_LEVELS + { + u8 LVL1_AddrMethod_plus_LunOrBusNumber; + u8 LVL1_LunOrTarget; + u8 LVL2_AddrMethod_plus_LunOrBusNumber; + u8 LVL2_LunOrTarget; + u8 LVL3_AddrMethod_plus_LunOrBusNumber; + u8 LVL3_LunOrTarget; + u8 LVL4_AddrMethod_plus_LunOrBusNumber; + u8 LVL4_LunOrTarget; + } SCSI_LUN_LEVELS_t, *pSCSI_LUN_LEVELS_t; + + /* + * Structure definition for data returned by REPORT LUNS cmd; + * LUN reporting parameter list format + */ + typedef struct LUN_REPORT + { + u32 LunListLength; + u32 Reserved; + SCSI_LUN_LEVELS_t LunInfo[1]; + } SCSI_LUN_REPORT_t, *pSCSI_LUN_REPORT_t; + +/**************************************************************************** + * + * Externals + */ + +/**************************************************************************** + * + * Public Typedefs & Related Defines + */ + +/**************************************************************************** + * + * Macros (embedded, above) + */ + +/**************************************************************************** + * + * Public Variables + */ + +/**************************************************************************** + * + * Public Prototypes (module entry points) + */ + + +/***************************************************************************/ +#endif diff --git a/xen/drivers/scsi/megaraid.c b/xen/drivers/scsi/megaraid.c index 13b0aded8a..44e9aa59f6 100644 --- a/xen/drivers/scsi/megaraid.c +++ b/xen/drivers/scsi/megaraid.c @@ -3355,8 +3355,6 @@ int megaraid_detect (Scsi_Host_Template * pHostTmpl) skip_id = (skip_id > 15) ? -1 : skip_id; } - printk (KERN_NOTICE "megaraid: " MEGARAID_VERSION); - memset (mega_hbas, 0, sizeof (mega_hbas)); /* Detect ROMBs first */ @@ -3378,7 +3376,11 @@ int megaraid_detect (Scsi_Host_Template * pHostTmpl) count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_PERC4_QC_VERDE, PCI_DEVICE_ID_PERC4_QC_VERDE, BOARD_QUARTZ); - mega_reorder_hosts (); + if (count) + printk (KERN_NOTICE "megaraid: " MEGARAID_VERSION); + + if (count) + mega_reorder_hosts (); #ifdef CONFIG_PROC_FS if (count) { -- 2.30.2